Java中的集合类主要由Collection和Map这两个接口派生而出,其中Collection接口又派生出三个子接口,分别是Set、List、Queue。所有的Java集合类,都是Set、List、Queue、Map这四个接口的实现类,这四个接口将集合分成了四大类:
Set代表无序的,元素不可重复的集合; List代表有序的,元素可以重复的集合; Queue代表先进先出(FIFO)的队列; Map代表具有映射关系(key-value)的集合,其所有的key是一个Set集合,即key无序且不能重复。 这些接口拥有众多的实现类,其中最常用的实现类有HashSet、TreeSet、ArrayList、LinkedList、ArrayDeque、HashMap、TreeMap等。
List与Set的对比
Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。 List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。
- 看到array,就要想到索引。
- 看到link,就要想到first,last。
- 看到hash,就要想到hashCode,equals.
- 看到tree,就要想到两个接口。Comparable,Comparator。
一、Set
1、Set集合的特点
- 无序性:Set 集合中的元素不按任何特定顺序排列,无法通过索引访问元素,即集合内部的元素顺序可能随时间和操作发生变化。
- 唯一性:Set 集合不允许包含重复的元素。判断元素是否重复的标准是基于元素的
.equals()方法。如果两个对象在.equals()方法下判断为相等,则 Set 集合中只会存储其中一个。 - 最大容量:理论上,Set 集合可以无限增长,直到受到可用内存限制为止。
- 注: Set没有索引,不能通过索引访问元素,所以不能使用普通for循环遍历,只能通过元素本身来访问
2、Set集合的主要实现类
HashSet:基于哈希表实现,具有良好的插入、删除和查找性能(平均时间复杂度为 O(1)),但不保证元素的迭代顺序。TreeSet:基于红黑树实现,元素自动排序(要么基于元素自身的自然排序,要么通过自定义 Comparator),插入、删除和查找性能为 O(log n),确保了集合内元素的排序顺序。LinkedHashSet:结合了 HashSet 和 LinkedList 的特性,它维护元素插入的顺序,同时仍然保证元素的唯一性。
3、HashSet
- 在元素类中重写hashCode方法和equals方法,依此来确保元素的唯一性
- 使用HashSet时,如果没有重写两个方法,则元素还是会重复录入,因为HashSet不知道判断两个对象相等的条件
- 重写时可以添加条件,比如name和age相等时,对象相等,则只会保留一个该数据的对象
- 如果存入的是字符串或整数等数据,则HashSet会自动判断唯一性
- 如果存入的是用户对象,则HashSet根据该对象的类中重写的hashCode方法和equals方法,来判断唯一性
4、LinkedHashSet
LinkedHashSet底层数据结构是哈希表和链表,根据元素的hashCode值来决定元素的存储位置,但它同时使用链表维护元素的次序,这样使得元素看起来是以插入的顺序保存的,也就是说当遍历集合LinkedHashSet集合里的元素时,集合将会按元素的添加顺序来访问集合里的元素。 输出集合里的元素时,元素顺序总是与添加顺序一致。但是LinkedHashSet依然是HashSet,因此它不允许集合重复。
5、TreeSet
TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态。TreeSet内部实现的是红黑树,默认整形排序为从小到大。不允许放入null值.
存入TreeSet集合中的元素要具备比较性
- 比较性要实现Comparable接口,重写该接口的compareTo方法
- TreeSet根据构造方法不同,分为自然排序(无参构造)和比较器排序(有参构造)
- 自然排序要求元素必须实现Compareable接口,并重写里面的compareTo()方法,元素通过比较返回的int值来判断排序序列,返回0说明两个对象相同,不需要存储
- 比较器排需要在TreeSet初始化是时候传入一个实现Comparator接口的比较器对象,或者采用匿名内部类的方式new一个Comparator对象,重写里面的compare()方法;
自然排序
- TreeSet是红黑树(二叉树),比较器用来给元素定位。如果比较器返回0,则元素重复,不添加,如果返回1或-1,则元素存储在二叉树的左或右孩子
- 自然排序,需要让元素所属的类实现Comparable接口,添加泛型,重写compareTo(T o)方法,当调用一个一个对象调用该方法与另一个对象进行比较时,obj1.compareTo(obj2)如果返回0表示两个对象相等;如果返回正整数则表明obj1大于obj2,如果是负整数则相反。
自定义排序
TreeSet集合有参构造方法使用比较器排序对元素进行排序
- 这个方法,是让容器具有比较性
- 当元素自身不具备比较性,或者自身具备的比较性不是所需要的。那么此时可以让容器自身具备。需要定义一个类实现接口Comparator,重写compare方法,并将该接口的子类实例对象作为参数传递给TreeMap集合的构造方法。
- 注意:当Comparable比较方式和Comparator比较方式同时存在时,以Comparator的比较方式为主;
- 注意:在重写compareTo或者compare方法时,必须要明确比较的主要条件相等时要比较次要条件。(假设姓名和年龄一直的人为相同的人,如果想要对人按照年龄的大小来排序,如果年龄相同的人,需要如何处理?不能直接return 0,因为可能姓名不同(年龄相同姓名不同的人是不同的人)。此时就需要进行次要条件判断(需要判断姓名),只有姓名和年龄同时相等的才可以返回0.)通过return 0来判断唯一性。
- 由于Comparator是一个函数式接口,因此还可以使用Lambda表达式来代替Comparator子类对象。
注意
- 大部分类在实现CompareTo(Object o)方法时,都需要将被比较对象obj强制类型转换成相同类型,因为只有相同的两个实例才会比较大小。
- 加入集合的类都必须实现Comparable接口,否则会引发ClassCastException异常。
- 不要修改已经存入集合的实例变量,这将导致它与其他对象的大小顺序发生改变,但TreeSet集合不会再次调整它们的顺序,这点和HashSet一样。
- 总结:如果希望TreeSet能正常工作,TreeSet只能添加同一种类型的对象
TreeSet如何确保唯一性?
对于TreeSet集合而言,它判断两个对象是否相等的唯一标准是:两个对象通过compareTo(Object obj)方法比较是否返回0,如果是0则认为对象相等,否则认为不相等。
Set小结
Set具有与Collection完全一样的接口,因此没有任何额外的功能,不像前面有两个不同的List。实际上Set就是Collection,只是行为不同。(这是继承与多态思想的典型应用:表现不同的行为)Set不保存重复的元素。 TreeSet不允许放入null值,HashSet允许放入一个null值 适用场景分析: HashSet是基于Hash算法实现的,其性能通常都优于TreeSet。为快速查找而设计的Set,我们通常都应该使用HashSet,在我们需要排序的功能时,我们才使用TreeSet。