摘要:集合判断两个元素的标准是两个对象通过方法比较相等,并且两个对象的方法返回值也相等。的集合元素也是有序的,以枚举值在类内的定义顺序来决定集合元素的顺序。是所有实现类中性能最好的,但它只能保存同一个枚举类的枚举值作为集合元素。
Set集合通常不能记住元素的添加顺序。Set不允许包含重复的元素。
Set集合不允许包含相同的元素,如果试图把两个相同的元素加入同一个Set集合中,则添加操作失败,add()方法返回false,且新元素不会被加入。
HashSet类HashSet按照Hash算法来存储集合中的元素,因此具有很好的存取和查询性能。
HashSet特点
不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化。
HashSet不是同步的,如果多个线程同时访问一个HashSet,假设有两个或者两个以上线程同时修改了HashSet集合时,则必须通过代码来保证其同步。
集合元素值可以是null;
当HashSet集合中存入一个元素时,HashSet会调用该对象的HashCode()方法来得到该对象的hashCode值,然后根据该hashCode值决定该对象在HashSet中的存储位置,如果有两个元素通过equals()方法比较返回true,但他们的hashCode()方法返回值不相等,HashSet将会把他们存在不同位置,依然可以添加成功。
HashSet集合判断两个元素的标准是两个对象通过equals()方法比较相等,并且两个对象的hashCode()方法返回值也相等。
即使两个A对象通过equals()方法比较返回true,但HashSet依然把他们当成两个对象,即使两个B对象的hashCode()方法返回相同值,但HashSet依然把他们当成两个对象。
当把一个对象放入HashSet中时,如果需要重写该对象对应类的equals()方法,则也应该重写其hashCode()方法,规则是:如果两个对象通过equals()方法比较返回true,这两个对象的hashCode值也应该相同。
如果两个对象通过equals()比较返回true,但这两个对象的hashCode()方法返回不同的hashCode值时,这将导致HashSet会把这两个对象保存在Hash表的不同位置,从而使两个对象都可以添加成功,这就与Set集合规则冲突了。
如果两个对象的hashCode()值返回的值相同,但他们通过equals()方法比较返回false时更麻烦。因为两个对象的hashCode值相同,HashSet将试图把他们保存在同一个位置,但又不行(否则将只剩下一个对象),所以实际上回在这个位置用链式结构来保存多个对象;而HashSet访问集合元素时也是根据元素的hashCode值来快速定位的,如果HashSet中两个以上的元素具有相同的hashCode值,将会导致性能下降。
Hash算法可以直接根据该元素的hashCode值计算出该元素的存储位置,从而快速定位该元素。
数组是所有能存储一组元素里最快的数据结构。
当从HashSet中访问元素时,HashSet先计算该元素的hashCode值,也就是调用该元素的hashCode()方法的返回值,然后直接到该hashCode值对应的位置去取出该元素。这就是HashSet速度很快的原因。
HashSet中每个能存储元素的“槽位slot”通常称为桶bucket,如果有多个元素的hashCode值相同,但他们通过equals方法比较返回false,就需要在一个桶里存放多个元素,就会导致性能下降。
重写hashCode方法的基本原则。
在程序运行过程中,同一个对象多次调用hashCode()方法应该返回相同的值。
当两个对象通过equals()方法比较返回true时,hashCode()应该也返回相同值。
对象中用作equals()方法比较标准的实例变量,都应该用于计算hashCode值。
当程序把可变对象添加到HashSet中之后,尽量不要去修改该集合元素中参与计算hashCode()’equals()的实例变量,否则将会导致HashSet无法争取操作这些集合。
LinkedHashSet类LinkedHashSet集合也是根据元素的hashCode值来决定元素的存储位置,但它同时使用链表维护元素的次序,LinkedHashSet将会按元素的添加顺序来访问集合里的元素。
同样不能允许集合元素重复,
public class LinkedHashSetTest { public static void main(String[] args) { CollectionTreeSet类collection = new LinkedHashSet<>(); collection.add("java"); collection.add("python"); //[java, python] System.out.println(collection); collection.remove("java"); collection.add("java"); //[python, java] System.out.println(collection); } }
TreeSet是StortedSet接口的实现类。TreeSet可以保证集合元素处于排序状态。
public class TreeSetTest { public static void main(String[] args) { TreeSettreeSet = new TreeSet<>(); treeSet.add("5"); treeSet.add("4"); treeSet.add("3"); treeSet.add("2"); treeSet.add("1"); System.out.println(treeSet); System.out.println(treeSet.first());//1 System.out.println(treeSet.last());//5 //返回集合中位于指定元素之前的元素 System.out.println(treeSet.lower("4"));//3 //返回集合中位于指定元素之后的元素 System.out.println(treeSet.higher("4"));//5 //返回此set的子集,由小于指定元素的元素组成 SortedSet headSet = treeSet.headSet("4"); System.out.println(headSet);//[1, 2, 3] //返回set的子集。由大于或者等于指定元素的元素组成 SortedSet tailSet = treeSet.tailSet("4"); System.out.println(tailSet);//[4, 5] } }
Tree并不是根据元素的插入顺序进行排序的,而是根据元素实际的大小来进行排序的。
与HashSet集合采用hash算法来决定元素的存储位置不同,TreeSet是采用红黑树的数据结构来存储集合元素,TreeSet支持两种排序方法,自然排序和定制排序。默认下TreeSet采用自然排序。
自然排序
TreeSet会调用集合元素的compareTo(Object o)方法来比较元素之间的大小关系,然后将集合元素按升序排序,这种方式就是自然排序。
如果试图把一个对象添加到TreeSet时,则该对象的类必须实现comparable接口。否则报错
public class TreeSetErrorTest { public static void main(String[] args) { USER user = new USER("", 2); TreeSettreeSet = new TreeSet<>(); //Exception in thread "main" java.lang.ClassCastException: setTest.USER cannot be cast to java.base/java.lang.Comparable treeSet.add(user); } }
当试图把一个对象添加到TreeSet集合时,TreeSet会调用该对象的comparaTo(Object o)方法与集合中的其他元素进行比较,这就要求集合中的其他元素与该元素时同一类的实例,也就是说,向TreeSet中添加的应该是同一个类的对象,否则也会引发ClassCastException异常。
public class TreeSetErrorTest2 { public static void main(String[] args) { TreeSet set = new TreeSet<>(); set.add(new String()); //Exception in thread "main" java.lang.ClassCastException: java.base/java.lang.String cannot be cast to java.base/java.util.Date set.add(new Date()); /* * 在添加String时,是没有错误的,当添加Date对象时,TreeSet就会调用该对象的comparaTo方法与集合中的其他元素进行比较-- * Date对象的comparaTo方法无法与字符串对象比较大小,所以引发异常 * */ } }
TreeSet只能添加同一种类型的对象,
当把一个对象加入TreeSet集合中时,TreeSet调用该对象的compareTo(Obejct o)方法与容器中的其他对象比较大小,然后根据红黑树结构找到他的存储位置,如果两个对象通过compareTo(Object o)方法比较相等,新对象将无法添加到TreeSet集合中。
TreeSet集合判断两个对象是够相等的唯一标准是:如果通过compareTo方法比较返回0,TreeSet则会认为他们相等,否则就认为他们不相等。
当需要把一个对象放入TreeSet中,重写该对象对应类的equals方法时,应保证该方法与compareTo方法有一致的结果,其规则是,如果两个对象通过equals方法比较返回true时,这两个对象通过compareTo方法比较应返回0;
TreeSet可以删除没有被修改实例变量,且不与其他被修改实例变量的对象重复的对象。P309
推荐不要修改放入HashSet和TreeSet集合中元素的关键实例变量。
定制排序public class MSort { public static void main(String[] args) { TreeSettreeSet = new TreeSet<>((o1,o2)->{ M m1 = o1; M m2 = o2; return Integer.compare(m1.a, m2.a); }); treeSet.add(new M(12)); treeSet.add(new M(1232)); treeSet.add(new M(121)); //[12, 121, 1232] System.out.println(treeSet); } }
当通过Comparator对象或Lambda表达式来实现TreeSet的定制排序时,依然不可以向TreeSet中添加类型不同的对象,否则会引发ClassCastException异常,使用定制排序时,TreeSet对集合元素排序不管集合元素本身的大小,而是由Comparator对象或Lambda表达式。负责集合元素的排序规则,TreeSet判断两个集合元素相等的标准是:通过Comparator比较两个元素返回了0,这样TreeSet不会把第二个元素添加到集合中。
EnumSet类EnumSet是一个转为枚举类设计的集合类,EnumSet中的所有元素都必须是指定枚举类型的枚举值,该枚举值在创建EnumSet时显示或隐式地指定。EnumSet的集合元素也是有序的,EnumSet以枚举值在Enum类内的定义顺序来决定集合元素的顺序。
EnumSet在内部以位向量的形式存储,这种存储形式非常紧凑高效,因此EnumSet对象占用内存很小,运行效率好,尤其是在进行批量操作的时候。
EnumSet集合不允许加入NULL;
public class SeasonEnumSetTest { public static void main(String[] args) { //创建一个EnumSet集合,集合元素是Season的全部枚举 EnumSetenumSet = EnumSet.allOf(Season.class); System.out.println(enumSet);//[SPTING, SUMMER, FALL, WINTER] //创建一个空集合,指定其集合元素是Season类的枚举类 EnumSet noneOf = EnumSet.noneOf(Season.class); System.out.println(noneOf);//[] noneOf.add(Season.FALL); noneOf.add(Season.WINTER); System.out.println(noneOf);//[FALL, WINTER] //利用现有枚举进行创建EnumSet集合 EnumSet of = EnumSet.of(Season.SPTING,Season.SUMMER); System.out.println(of);//[SPTING, SUMMER] //创建几个从begin到end之间的枚举作为新集合的元素 EnumSet range = EnumSet.range(Season.SUMMER, Season.WINTER); System.out.println(range);//[SUMMER, FALL, WINTER] //range与complementof枚举值和是Season的全部枚举 EnumSet complementOf = EnumSet.complementOf(range); System.out.println(complementOf);//[SPTING] } }
当试图复制一个Collection集合里的元素来创建EnumSet集合时,必须保证Collection集合里的所有元素都是同一个枚举类的枚举值。
Set实现类的性能分析HashSet的性能总是比TreeSet好,特别是最常用的添加,查询元素等操作,因为TreeSet需要额外的红黑树算法来维护集合元素的次序,只有当需要一个保持排序的Set时,才应该使用TreeSet,否则都应该使用HashSet。
LinkedHashSet对于普通的插入,删除操作,LinkedHashSet比HashSet要略慢一点,这是由维护链表所带来的额外开销造成的,但由于有了链表,遍历LinkedHashSet会更快。
EnumSet是所有Set实现类中性能最好的,但它只能保存同一个枚举类的枚举值作为集合元素。
Set的三个实现类HashSet,TreeSet和EnumSet都是线程不安全的。
如果有多个线程同时访问Set集合那么需要手动保证该Set集合的同步性。
Collections.synchronizedSortedSet(new TreeSet(...));
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/69316.html
Set接口 Set是一个不能包含重复元素的Collection,它模拟了数学集抽象,Set接口仅包含从Collection继承的方法,并添加禁止重复元素的限制,Set还为equals和hashCode操作的行为添加了一个更强的契约,允许Set实例有意义地进行比较,即使它们的实现类型不同,如果两个Set实例包含相同的元素,则它们是相等的。 Java平台包含三个通用的Set实现:HashSet、Tre...
集合接口 核心集合接口封装了不同类型的集合,如下图所示,这些接口允许独立于其表示的细节来操纵集合,核心集合接口是Java集合框架的基础,如下图所示,核心集合接口形成层次结构。 showImg(https://segmentfault.com/img/bVbntJW?w=402&h=146); Set是一种特殊的Collection,SortedSet是一种特殊的Set,依此类推,另请注意,层次结构...
摘要:一概述集合是引入的新的内置对象类型,其特点同数学意义的集合,即集合内所有元素不重复元素唯一。数组集合对比数组和集合,数组可以加入重复数据,而集合的所有元素是唯一的不允许重复。因此,适合临时存放一组对象,以及存放跟对象绑定的信息。 本文同步带你入门 带你入门 JavaScript ES6 (五) 集合,转载请注明出处。 前面我们学习了: for of 变量和扩展语法 块作用域变量和解构...
摘要:第三阶段常见对象的学习集合框架集合在实际需求中,我们常常会遇到这样的问题,在诸多的数据中,通过其编号来寻找某一些信息,从而进行查看或者修改,例如通过学号查询学生信息。面试题和的区别是单列集合的顶层接口,有子接口和。 第三阶段 JAVA常见对象的学习 集合框架——Map集合 showImg(https://segmentfault.com/img/remote/1460000019683...
阅读 2061·2021-11-11 16:54
阅读 1045·2021-10-12 10:12
阅读 380·2019-08-30 15:43
阅读 648·2019-08-29 13:15
阅读 1078·2019-08-29 13:12
阅读 1527·2019-08-26 12:09
阅读 1657·2019-08-26 10:24
阅读 2254·2019-08-26 10:15