一、单列集合(Collection)
Collection是单列集合的底层接口,它的实现类又分为有序集合(List)和无序集合(Set)
List的经典实现类为:有序,可重复,有索引
(1)ArrayList(基于数组):查询效率高,修改和插入效率低
实现原理:
1.在使用空参构造创建ArrayList集合时,底层会初始化一个长度为0的数组;
2.在第一次添加数据的时候会创建一个新的长度为10的数组用以实际存储数据;
3.在持续添加过程中,添加的数量超出现有数组的长度,底层会自动创建一个长度为原数组长度1.5倍的新数组,先将原数组数据转存过来,再添加新数据;
(2)LinkedList(基于双向链表):查询效率低,修改和插入效率高
实现原理:
底层基于双向链表实现,在存储的元素中都标记上次元素以及下一个元素的地址
Set的经典实现类:无序,不可重复,无索引
HashSet:无序,不可重复,无索引
LinkedSet:有序,无索引,不可重复
TreeSet:排序,不重复,无索引
(1)HashSet
JDK8之前:数组+链表
JDK8开始:数组+链表+红黑树
具体实现特点:
1、创建HashSet对象时底层会先生成一个长度为16,扩容因子为0.75(存满75%自动扩容两倍)的数组
2、当数据需要存储时,底层进行计算,对象的哈希值对数组长度取余,即为存入位置的索引
3、判断索引位是否存在元素,若无,则直接存入数组,若有则需equals比较,相同则不存(不重复原理),不同则挂入
4、JDK8之前,新元素存入数组位置,老元素挂在新元素下,JDK8开始新元素直接挂在老元素下面
5、JDK8之后的特性:若集合中数组长度超过64,链表长度超过8则自动生成红黑树
我们发现往HashSet集合中存储元素时,底层调用了元素的两个方法:一个是hashCode方法获取元素的hashCode值(哈希值);另一个是调用了元素的equals方法,用来比较新添加的元素和集合中已有的元素是否相同。
-
只有新添加元素的hashCode值和集合中以后元素的hashCode值相同、新添加的元素调用equals方法和集合中已有元素比较结果为true, 才认为元素重复。
-
如果hashCode值相同,equals比较不同,则以链表的形式连接在数组的同一个索引为位置(如上图所示)
在JDK8开始后,为了提高性能,当链表的长度超过8时,就会把链表转换为红黑树,如下图所示:
(2)LinkedHashSet:
LinkedHashSet是在HashSet的基础上,底层增加了一层链表,让数据可以记录下一个数据的位置,实现顺序存取
(3)TreeSet
TreeSet集合的特点是可以对元素进行排序,但是必须指定元素的排序规则。
如果往集合中存储String类型的元素,或者Integer类型的元素,它们本身就具备排序规则,所以直接就可以排序。
Set<Integer> set1= new TreeSet<>();
set1.add(8);
set1.add(6);
set1.add(4);
set1.add(3);
set1.add(7);
set1.add(1);
set1.add(5);
set1.add(2);
System.out.println(set1); //[1,2,3,4,5,6,7,8]
Set<Integer> set2= new TreeSet<>();
set2.add("a");
set2.add("c");
set2.add("e");
set2.add("b");
set2.add("d");
set2.add("f");
set2.add("g");
System.out.println(set1); //[a,b,c,d,e,f,g]
如果往TreeSet集合中存储自定义类型的元素,比如说Student类型,则需要我们自己指定排序规则,否则会出现异常。
我们想要告诉TreeSet集合按照指定的规则排序,有两种办法:
第一种:让元素的类实现Comparable接口,重写compareTo方法
第二种:在创建TreeSet集合时,通过构造方法传递Compartor比较器对象
排序方式1:我们先来演示第一种排序方式
//第一步:先让Student类,实现Comparable接口
//注意:Student类的对象是作为TreeSet集合的元素的
public class Student implements Comparable<Student>{
private String name;
private int age;
private double height;
//无参数构造方法
public Student(){}
//全参数构造方法
public Student(String name, int age, double height){
this.name=name;
this.age=age;
this.height=height;
}
//...get、set、toString()方法自己补上..
//第二步:重写compareTo方法
//按照年龄进行比较,只需要在方法中让this.age和o.age相减就可以。
/*
原理:
在往TreeSet集合中添加元素时,add方法底层会调用compareTo方法,根据该方法的
结果是正数、负数、还是零,决定元素放在后面、前面还是不存。
*/
@Override
public int compareTo(Student o) {
//this:表示将要添加进去的Student对象
//o: 表示集合中已有的Student对象
return this.age-o.age;
}
}
排序方式2:接下来演示第二种排序方式
//创建TreeSet集合时,传递比较器对象排序
/*
原理:当调用add方法时,底层会先用比较器,根据Comparator的compare方是正数、负数、还是零,决定谁在后,谁在前,谁不存。
*/
//下面代码中是按照学生的年龄升序排序
Set<Student> students = new TreeSet<>(new Comparator<Student>{
@Override
public int compare(Student o1, Student o2){
//需求:按照学生的身高排序
return Double.compare(o1,o2);
}
});
//创建4个Student对象
Student s1 = new Student("至尊宝",20, 169.6);
Student s2 = new Student("紫霞",23, 169.8);
Student s3 = new Student("蜘蛛精",23, 169.6);
Student s4 = new Student("牛魔王",48, 169.6);
//添加Studnet对象到集合
students.add(s1);
students.add(s2);
students.add(s3);
students.add(s4);
System.out.println(students);