集合体系结构
Collection是单列集合的祖宗,它规定的方法(功能)是全部单列集合都会继承的。
collection集合体系
Collection的常用方法
java
package com.itchinajie.d1_collection;
import java.util.ArrayList;
import java.util.HashSet;
/*
* 目标:认识Collection体系的特点。
* */
public class CollectionTest1 {
public static void main(String[] args) {
//简单确认一下Collection集合的特点。
ArrayList<String> list=new ArrayList<>();//有序 可重复 有索引
list.add("java1");
list.add("java2");
list.add("java1");
list.add("java2");
System.out.println(list);
HashSet<String>set = new HashSet<>();
set.add("java1");
set.add("java2");
set.add("java1");
set.add("java2");
set.add("java3");
System.out.println(set);
}
}
package com.itchinajie.d1_collection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
public class CollectionTest2API {
public static void main(String[] args) {
Collection<String> c = new ArrayList<>();// 多态写法
//1.public boolean add(Ee):添加元素,添加成功返回true。
c.add("java1");
c.add("java1");
c.add("java2");
c.add("java2");
c.add("java3");
System.out.println(c);
//2.public void clear():清空集合的元素。
//c.clear();
//system.out.println(c);
//3.public boolean isEmpty():判断集合是否为空是空返回true,反之。
System.out.println(c.isEmpty()); // false
//4.public int size():获取集合的大小。
System.out.println(c.size());
//5.public boolean contains(object obj):判断集合中是否包含某个元素。
System.out.println(c.contains("java1")); // true
System.out.println(c.contains("Java1")); // false
//6.public boolean remove(Ee):删除某个元素:如果有多个重复元素默认删除前面的第一个!
System.out.println(c.remove( "java1"));
System.out.println(c);
//7.public Object [] toArray();把集合转换成数组
Object[] arr = c.toArray();
System.out.println(Arrays.toString(arr));
String[] arr2 = c.toArray(new String[c.size()]);
System.out.println(Arrays.toString(arr2));
System.out.println("------------------------------------");
//把一个集合的全部数据倒入到另一个集合中去。
Collection<String>c1 = new ArrayList<>();
c1.add("java1");
c1.add("java2");
Collection<String> c2 = new ArrayList<>();
c2.add("java3");
c2.add("java4");
c1.addAll(c2);//就是把c2集合的全部数据倒入到c1集合中去。
System.out.println(c1);
System.out.println(c2);
}
}
Collection的遍历方式
迭代器
迭代器是用来遍历集合的专用方式(数组没有迭代器),在到ava中迭代器的代表是Iterator。
java
package com.itchinajie.d2_collection_traverse;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/*
* 目标:Collection集合遍历方式一:使迭代器Iterator遍历
* */
public class CollectionDemo1 {
public static void main(String[] args) {
Collection<String> c = new ArrayList<>();
c.add("赵敏");
c.add("小昭");
c.add("素素");
c.add("灭绝");
System.out.println(c);
//c=[赵敏,小昭,素素,灭绝]
// it
//使用迭代器遍历集合
//1、从集合对象中获取迭代器对象。
Iterator<String> it = c.iterator();
// System.out.println(it.next());
// System.out.println(it.next());
// System.out.println(it.next());
// System.out.println(it.next());
//System.out.println(it.next());//出现异常的
//2、我们应该使用循环结合迭代器遍历集合。
while (it.hasNext()){
// String ele = it.next();
// System.out.println(ele);
System.out.println(it.next());
}
}
}
增强for
增强for可以用来遍历集合或者数组。
增强for遍历集合,本质就是迭代器遍历集合的简化写法。
java
package com.itchinajie.d2_collection_traverse;
import java.util.ArrayList;
import java.util.Collection;
/*
* 目标:Collection集合遍历方式二:增强for
* */
public class CollectionDemo2 {
public static void main(String[] args) {
Collection<String> c = new ArrayList<>();
c.add("赵敏");
c.add("小昭");
c.add("素素");
c.add("灭绝");
System.out.println(c);
//c=[赵敏,小昭,素素,灭绝]
// ele
//使用增强for遍历集合或者数组。
for (String ele : c){
System.out.println(ele);
}
String[] names = {"迪丽热巴","古力娜扎","稀奇哈哈"};
for (String name : names){
System.out.println(name);
}
}
}
Lambda表达式
得益于刊DK8开始的新技术Lambda表达式,提供了一种更简单、更直接的方式来遍历集合。
java
package com.itchinajie.d2_collection_traverse;
import java.util.ArrayList;
import java.util.Collection;
/*
* 目标:Collection集合的遍历方式三:JDK8开始新增的lambda表达式(forEach方法)
* */
public class CollectionDemo3 {
public static void main(String[] args) {
Collection<String> c = new ArrayList<>();
c.add("赵敏");
c.add("小昭");
c.add("殷素素");
c.add("周芷若");
System.out.println(c);
//[赵敏,小昭,般素素,周芷若]
// s
//default void forEach(Consumer<?superT>action): 结合Lambda表达式遍历集合:
// c.forEach(new Consumer<String>() {
// @Override
// public void accept(String s) {
// System.out.println(s);
// }
// });
// //简化
// c.forEach((String s) -> {
// System.out.println(s);
// });
//简化
// c.forEach( s -> {
// System.out.println(s);
// });
//简化
// c.forEach( s -> System.out.println(s));
c.forEach( System.out::println);
}
}
List系列集合
List集合的特有方法 List集合因为支持索引,所以多了很多与索引相关的方法,当然,Collection的功能List也都继承了。
List集合的特有方法
java
package com.itchinajie.d3_collection_list;
import java.util.ArrayList;
import java.util.List;
/**
目标:掌握List系列集合的特点,以及其提供的特有方法。
*/
public class ListTest1 {
public static void main(String[] args) {
//1.创建一个ArrayList集合对象(有序、可重复、有索引)
List<String> list = new ArrayList<>();// 一行经典代码
list.add("蜘蛛精");
list.add("至尊宝");
list.add("至尊宝");
list.add("牛夫人");
System.out.println(list);//【蜘蛛精,至尊宝,至尊宝,牛夫人]
//2.public void add(int index,E element):在某个索引位置插入元素。
list.add( 2,"紫霞仙子");
System.out.println(list);
//3.public E remove(int index):根据索引删除元素,返回被删除元素
System.out.println(list.remove(2));
System.out.println(list);
//4.public E get(int index):返回集合中指定位置的元素。
System.out.println(list.get(3));
//5.public E set(int index,E element):修改索引位置处的元素,修改成功后,会返回原来的数据
System.out.println(list.set(3,"牛魔王"));
System.out.println(list);
}
}
遍历方式
List集合支持的遍历方式:
1、for循环(因为List集合有索引)
2、迭代器
3、增强for循环(foreach遍历)
4、Lambda表达式
java
package com.itchinajie.d3_collection_list;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/*
* List集合支持的遍历方式:
1、for循环(因为List集合有索引)
2、迭代器
3、增强for循环
4、Lambda表达式
* */
public class ListTest2 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("糖宝宝");
list.add("蜘蛛精");
list.add("至尊宝");
//(1)for循环
for (int i=0;i<list.size();i++){
//i=012
String s = list.get(i);
System.out.println(s);
}
//(2)迭代器
Iterator<String> it = list.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
//(3)增强for循环(foreach循环)
for (String s : list) {
System.out.println(s);
}
//(4)JDK1.8开始之后的Lambda表达式
list.forEach(s -> {
System.out.println(s);
});
}
}
ArrayList集合的底层原理
特点
1、基于数组实现的
2、查询速度快(注意:是根据索引查询数据快):查询数据通过地址值和索引定位,查询任意数据耗时相同。
3、删除效率低:可能需要把后面很多的数据进行前移。
4、添加效率极低:可能需要把后面很多的数据后移,再添加元素;或者也可能需要进行数组的扩容。
底层原理
1、利用无参构造器创建的集合,会在底层创建一个默认长度为0的数组;
2、添加第一个元素时,底层会创建一个新的长度为10的数组;
3、存满时,会扩容1.5倍;
4、如果一次添加多个元素,1.5倍还放不下,则新创建数组的长度以实际为准。
ArrayList集合的应用原理
1、ArrayList适合:根据索I查询数据 ,比如根据随机索引数据(高效)!或者数据量 不是很大时!
2、ArrayList不适合:数据量大的同时 ,又要频繁的进行增删操作!
LinkedList集合的底层原理
基于双链表实现的。
什么是链表?有啥特点?
单项链表:链表的特点1: 查询慢,无论查询哪个数据都要从头开始找;
链表的特点2: 链表增删相对快。
**双向链表(基于双链表实现):基于双链表实现的。
特点:查询慢,增删相对较快,**但对首尾元素进行增删改查的速度是极快的。
新增首尾操作的特有方法
LinkedList集合的应用场景
1、用来设计队列
2、用来设计栈
java
package com.itchinajie.d3_collection_list;
import java.util.LinkedList;
/**
目标:掌握List系列集合的特点,以及其提供的特有方法。
*/
public class ListTest3 {
public static void main(String[] args) {
//1、创建一个队列。
LinkedList<String> queue = new LinkedList<>();
//入队
queue.addLast("第1号人");
queue.addLast("第2号人");
queue.addLast("第3号人");
queue.addLast("第4号人");
System.out.println(queue);
//出队
System.out.println(queue.removeFirst());
System.out.println(queue.removeFirst());
System.out.println(queue.removeFirst());
System.out.println(queue);
System.out.println("------------------------------------");
//2、创建一个栈对象。
LinkedList<String> stack = new LinkedList<>();
//压栈
// stack.addFirst("第1颗子弹");
// stack.addFirst("第2颗子弹");
// stack.addFirst("第3颗子弹");
// stack.addFirst("第4颗子弹");
stack.push("第1颗子弹");
stack.push("第2颗子弹");
stack.push("第3颗子弹");
stack.push("第4颗子弹");
System.out.println(stack);
//出栈
// System.out.println(stack.removeFirst());
// System.out.println(stack.removeFirst());
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack);
}
}
set系列集合
java
package com.itchinajie.d4_collection_set;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
/*
* 目标:整体了解一下Set系列集合的特点
* */
public class SetTest1 {
public static void main(String[] args) {
//1、创建一个Set集合的对象
//Set<Integer>set =new HashSet<>();//创建了一个HashSet的集合对象。 一行经典代码 HashSet:无序不重复无索引,
// 不支持索引操作,一般只会无序一次
// Set<Integer> set = new LinkedHashSet<>();//有序 不重复 无索引
Set<Integer> set =new TreeSet<>();//可排序(升序) 不重复 无索引
set.add(666);
set.add(555);
set.add(555);
set.add(888);
set.add(888);
set.add(777);
set.add(777);
System.out.println(set);
}
}
HashSet
哈希值
就是一个int类型的数值,Java中每个对象都有一个哈希值。
Java中的所有对象,都可以调用Obejct类提供的hashCode方法,返回该对象自己的哈希值。
java
package com.itchinajie.d4_collection_set;
/**
*目标:了解一下哈希值。
*Java中的所有对象,都可以调用Obejct类提供的hashCode方法,返回该对象自己的哈希值。
* public int hashcode():返回对象的哈希值。
*同一个对象多次调用hashCode方法返回的哈希值是相同的。
*不同的对象,它们的哈希值一般不相同,但也有可能会相同(哈希碰撞)。
*/
public class SetTest2 {
public static void main(String[] args) {
Student s1 = new Student("蜘蛛精",25,169.5);
Student s2 = new Student("紫霞",24,166.5);
System.out.println(s1.hashCode());
System.out.println(s1.hashCode());
System.out.println(s2.hashCode());
String str = new String("abc");
String str2 = new String("acD");
System.out.println(str.hashCode());
System.out.println(str2.hashCode());
}
}
对象哈希值的特点
同一个对象多次调用hashCode()方法返回的哈希值是相同的。
不同的对象,它们的哈希值一般不相同,但也有可能会相同(哈希碰撞)。
HashSet的底层原理
了解一下数据结构(树)
1、普通二叉树(无用)
2、二叉查找树(二叉排序树)
小的存左边, 大的存右边, 一样的不存。
3、平衡二叉树(java常用)
4、java用的红黑树
深入理解HashSet集合去重复的机制。
HashSet集合默认不能对内容一样的两个不同对象去重复!
比如内容一样的两个学生对象存入到HashSet集合中去,HashSet集合是不能去重复的!
如何让HashSet集合能够实现对内容一样的两个不同对象也能去重复???
如果希望Set集合认为2个内容一样的对象是重复的 必须重写对象的hashCode()和equals()方法。
java
package com.itchinajie.d4_collection_set;
import java.util.Objects;
public class Student implements Comparable<Student>{
private String name;
private int age;
private double height;
//方式一:让自定义的类(如学生类)实现Comparable接口,重写里面的compareTo方法来指定比较规则。
@Override
public int compareTo(Student o) {
//约定1: 如果左边的对象 大于 右边对象 请您返回正整数
//约定2: 如果左边的对象 小于 右边对象 请您返回负整数
//约定3: 如果左边的对象 等于 右边对象 请您返回0
//按照年龄升序排序
// if (this.age > o.age) {
// return 1;
// }else if (this.age < o.age){
// return -1;
// }
// return 0;
//return this.age - o.age;//升序
return o.age - this.age;//降序
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public Student() {
}
public Student(String name, int age, double height) {
this.name = name;
this.age = age;
this.height = height;
}
//容一样就只要两个对象内返回true
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Double.compare(height, student.height) == 0 && Objects.equals(name, student.name);
}
//只要两个对象内容一样,返回的哈希值就是一样的。
@Override
public int hashCode() {
return Objects.hash(name, age, height);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", height=" + height +
'}';
}
}
package com.itchinajie.d4_collection_set;
import java.util.HashSet;
import java.util.Set;
public class SetTest3 {
public static void main(String[] args) {
Set<Student> students = new HashSet<>();
Student s1 = new Student("至尊宝", 28, 169.6);
Student s2 = new Student("蜘蛛精", 23, 169.6);
Student s3 = new Student("蜘蛛精", 23, 169.6);
System.out.println(s2.hashCode());
System.out.println(s3.hashCode());
Student s4 = new Student("牛魔王", 48, 169.6);
students.add(s1);
students.add(s2);
students.add(s3);
students.add(s4);
System.out.println(students);
}
}
LinkedHashSet
特点:有序、不重复、无索引。
底层原理
TreeSet
特点:不重复、无索引、可排序(默认升序排序,按照元素的大小,由小到大排序。
底层是基于红黑树实现的排序。
java
package com.itchinajie.d4_collection_set;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
/*
* 目标:掌握TreeSet集合的使用
* */
public class SetTest4 {
public static void main(String[] args) {
Set<Integer> set1 = new TreeSet<>();
set1.add(6);
set1.add(5);
set1.add(5);
set1.add(7);
System.out.println(set1);
//方式一:让自定义的类(如学生类)实现Comparable接口,重写里面的compareTo方法来指定比较规则。
//方式二:通过调用TreeSet集合有参数构造器,可以设置Comparator对像(比较器对象,用于指定比较规则)。
// new 一个TreeSet的比较器对象的有参构造器,生成一个比较器对象,然后重写对象的匿名内部类
//day04_oop -> src -> com.itchinajie -> d5_arrays 中讲过自定义排序规则
//TreeSet就近选择自己自带的比较器对象进行排序
Set<Student> students = new TreeSet<>(( o1, o2) -> Double.compare(o1.getHeight(), o2.getHeight()));
students.add(new Student("蜘蛛精",23,169.7));
students.add(new Student("紫霞",22,169.8));
students.add(new Student("至尊宝",26,165.5));
students.add(new Student("牛魔王",22,183.5));
System.out.println(students);
}
}
自定义排序规则
小结
注意:集合的并发修改异常
java
package com.itchinajie.d5_collection_exception;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class collection_exception {
public static void main(String[] args) {
//需求:找出集合中全部带"李"的名字,并从集合中删除。
List<String> list = new ArrayList<>();
list.add("王麻子");
list.add("小李子");
list.add("李爱花");
list.add("张全蛋");
list.add("晓李");
list.add("李玉刚");
System.out.println(list);
//[王麻子,小李子,李爱花,张全蛋,晓李,李玉刚]
//System.out.println("--------------------------------------");
//需求:找出集合中全部带"李"的名字,并从集合中删除。
// Iterator<String>it = list.iterator();
// while (it.hasNext()){
// String name = it.next();
// if(name.contains("李")){
// list.remove(name);
// }
// }
// System.out.println(list);
//报错
//System.out.println("--------------------------------------");
//使用for循环遍历集合并删除集合中带李字的名字
//[王麻子,小李子,李爱花,张全蛋,晓李,李玉刚]
// for (int i=0;i<list.size();i++){
// String name = list.get(i);
// if(name.contains("李")){
// list.remove(name);
// }
// }
// System.out.println(list);
//出bug,需要在他的基础上加个i--
//System.out.println("--------------------------------------");
//怎么解决呢?
//使用for循环遍历集合并刷除集合中带李字的名字
//[王麻子,小李子,李爱花,张全蛋,晓李,李玉刚]
//i
// for (int i = 0;i<list.size();i++){
// String name = list.get(i);
// if(name.contains("李")) {
// list.remove(name);
// i--;
// }
// }
// System.out.println(list);
//加个i--也可以
//倒着删除也可以
System.out.println("--------------------------------------");
//用迭代器正常删除的方式
需求:找出集合中全部带"李"的名字,并从集合中删除。
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String name = it.next();
if (name.contains("李")) {
//List.remove(name);//并发修改异常的错误。
it.remove();//删除迭代器当前遍历到的数据,每删除一个数据后,相当于也在底层做了i--
}
}
System.out.println(list);
//System.out.println("--------------------------------------");
//使用增强for循环遍历集合并删除数据,没有办法解决bug的
//由于增强for循环遍历集合就是迭代器遍历集合的简化写法,因此,使用增强fo循环遍历集合,又在同时删
//除集合中的数据时,程序也会出现并发修改异常的错误
// for (String name : list){
// if(name.contains("")){
// list.remove(name);
// }
// }
// System.out.println(list);
//System.out.println("--------------------------------------");
//使用lambda表达式也不行
// list.forEach(name ->{
// if(name.contains("李")){
// list.remove(name);
// }
// });
// System.out.println(list);
}
}
(本章图片均来自于黑马程序员视频)