• Set接口

  前面已经简绍过Set集合,它类似于一个罐子,一旦把对象'丢进'Set集合,集合里多个对象之间没有明显的顺序。Set集合于Collection基本上完全一样,它没有提供任何额外的方法。

  Set集合不容许包含相同的元素,如果试图把两个相同元素加入到同一个Set集合中,则添加操作失败,add方法返回false,且新元素不会被加入。

  Set判断两个对象是否相同不是使用==运算符,而是根据equals方法。也就是说,只要两个对象用equals方法比较返回true,Set就不会接受这两个对象,反之,只要两个对象用equals方法比较返回false,Set就会接受这两个对象(甚至这两个对象是同一个对象,Set也可把他们当成俩个对象处理),下面是Set的使用案例。

  1. public class Test {
  2. public static void main(String[] args){
  3. Set set = new HashSet();
  4. boolean a = set.add(new String("语文"));
  5. boolean b = set.add(new String("语文"));
  6. //打印结果为true
  7. System.out.println(a);
  8. //打印结果为false
  9. System.out.println(b);
  10. /*
  11. * 打印结果为[语文];
  12. * 因为两个字符串通过equals方法比较返回为true(String类默认重写了Object中equals方法),所以第二次添加失败
  13. */
  14. System.out.println(set);
  15. }
  16. }

  从上面程序中可以看出,books集合两次添加的字符串对象明显不是同一个对象(因为两次都调用了new关键字来创造字符串对象),这两个字符串对象使用==运算符判断肯定返回false,但它们通过equals方法比较将返回true,所以添加失败。最后输出set集合时,将看到输出结果只有一个元素。

  上面介绍的是Set集合的通用知识,完全适合HashSet、TreeSet和EnumSet三个实现类。

  • HashSet类

  HashSet具有以下特点:

    1. HashSet具有很好的对象检索性能,当从HashSet中查找某个对象时,Java系统首先调用对象的hasCode方法获得该对象的哈希码,然后根据哈希码找到对应的存储区域,最后取出该存储区域的每个元素与该对象进行equals方法的比较,这样不用遍历集合中的所有元素就可以得到结论。
    2. HashSet存储对象的效率相对要低些,因为向HashSet集合中添加对象的时候,首先要计算出来对象的哈希码和根据这个哈希码来确定对象在集合中的存放位置。
    3. 不能保证排列的顺序,顺序有可能发生改变。
    4. HashSet不是同步的,如果多个线程同时访问一个Set集合,如果多个线程同时访问一个HashSet集合,如果有2条或者2条以上线程同时修改了HashSet集合时,必须通过代码来保证其同步。
    5. HashSet集合元素可以是null。

  HashSet还有一个子类LinkedHashSet,LinkedHashSet集合也是根据元素hashCode值来决定元素存储位置,但它同时使用链表维护元素的次序,这样使的元素看起来是以插入的顺序保存的。也就是说当遍历LinkedHashSet集合里的元素时,HashSet将会按元素的添加顺序来访问集合里的元素。

  LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet的性能,但是在迭代访问Set里的全部元素时,将有很好的性能,因为它以列表来维护内部顺序。

  1. public class Test {
  2. public static void main(String[] args){
  3. LinkedHashSet books = new LinkedHashSet();
  4. books.add("语文");
  5. books.add("数学");
  6. books.add("英语");
  7. //删除语文
  8. books.remove("语文");
  9. //重新添加
  10. books.add("语文");
  11. //打印结果为[数学, 英语, 语文]
  12. System.out.println(books);
  13.  
  14. }
  15. }

  上面的集合里,元素的顺序正好与添加顺序一致。

  • TreeSet类

  TreeSet是SortedSet接口的唯一实现(SortedSet接口继承Set接口),正如SortedSet名字所暗示的,TreeSet可以确保集合元素处于排序状态。与前面的HashSet集合相比,TreeSet还提供了如下几个额外方法:

    1. Comparator comparator(); //返回当前Set使用的Comparator,或者返回null,表示以自然方式排序。
    2. Object first();   //第一个;返回集合中的第一个元素。
    3. Object last();   //最后一个;返回集合中的最后一个元素。
    4. Object lower(Object o);    //前一个;返回集合中位于指定元素之前的元素(即小于指定元素的最大元素,参考元素不需要是TreeSet的元素)。
    5. Object higher(Object o);  //后一个;返回集合中位于指定元素之后的元素(即大于指定元素的最小元素,参考元素不需要是TreeSet的元素)。
    6. SortedSet subSet(fromElement, toElement);    //返回此Set的子集合,范围从fromElement(包含)到toElement(不包含)。
    7. SortedSet headSet(toElement);  //返回此set的子集,由小于toElement的元素组成。
    8. SortedSet tailSet(fromElement);    //返回此set的子集,由大于或等于fromElement的元素组成。
  1. public class Test {
  2. public static void main(String[] args){
  3. TreeSet<Integer> nums = new TreeSet<Integer>();
  4. nums.add(3);
  5. nums.add(1);
  6. nums.add(5);
  7. nums.add(-9);
  8. //1.返回第一个元素
  9. Integer first = nums.first();
  10. //打印结果为-9
  11. System.out.println(first);
  12. //2.返回最后一个元素
  13. Integer last = nums.last();
  14. //打印结果为5
  15. System.out.println(last);
  16. //3.返回上一个
  17. Integer lower = nums.lower(2);
  18. //打印结果为1
  19. System.out.println(lower);
  20. //4.返回下一个
  21. Integer higher = nums.higher(2);
  22. //打印结果为3
  23. System.out.println(higher);
  24. //5.返回小于3的子集,不包含3
  25. SortedSet<Integer> headSet = nums.headSet(3);
  26. //打印结果[-9, 1]
  27. System.out.println(headSet);
  28. //6.返回大于等于3的子集,包含3
  29. SortedSet<Integer> tailSet = nums.tailSet(3);
  30. //打印结果[3, 5]
  31. System.out.println(tailSet);
  32. //7.打印整个集合结果为[-9, 1, 3, 5]
  33. System.out.println(nums);
  34.  
  35. }
  36. }

  根据上面程序的运行结果可看出,TreeSet并不是根据元素的插入顺序进行排序,而是根据元素实际值来进行排序的。

  与HashSet集合采用的hash算法来决定元素的存储位置不同,TreeSet采用红黑树的数据结构对元素进行排序。那么TreeSet进行排序是怎么样的呢?TreeSet支持两种排序方法:自然排序和定制排序。默认情况下,TreeSet采用自然排序。

  • 自然排序

  Java提供了一个Comparable接口,该接口里定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现该接口的类必须实现该方法,实现了该接口的类的对象就可以比较大小了。当一个对象调用该方法与另一个对象进行比较,例如obj1.compareTo(obj2); 如果该方法返回0,则表明这两个对象相等;如果该方法返回一个正整数,则表明obj1大于obj2;如果该方法返回一个负整数,则表明obj1小于obj2。

  Java的一些常用类已经实现了Comparable接口,并提供了比较大小的标准, 下面是实现了Comparable接口的常用类:

    1. BigDecimal、BigInteger以及所有数值型对应包装类:按它们对象的数值大小进行比较。
    2. Character :按字符的Unicode值进行比较。
    3. Boolean : true对应的包装类实例大于false对应的包装类实例。
    4. String : 按字符串中字符的Unicode值进行比较。
    5. Date、Time : 后面的时间、日期比前面的日期时间大。

如图所示:Integer类实现了Comparable接口:

  由于上边的Integer类实现了Comparable接口,故TreeSet会调用集合元素的compareTo(Object o)方法来比较元素之间的大小关系,然后将集合元素按升序排列,这种方式就是自然排序。如果试图把一个对象添加进TreeSet时,则该对象的类必须实现Comparable接口,否则程序将会抛出ClassCastException异常。代码如下:

  1. class Person{
  2.  
  3. }
  4. public class Test {
  5. public static void main(String[] args){
  6. TreeSet<Person> persons = new TreeSet<Person>();
  7. persons.add(new Person());
  8. System.out.println(persons);
  9. }
  10. }

以上代码将会抛出:

  • 定制排序

  TreeSet的自然排序是根据集合元素的大小,TreeSet将它们以升序排列。如果需要完成定制排序,例如以降序排列,则可以使用Comparator接口的帮助。该接口里包含了一个int compare(T o1, T o2)方法,该方法用于比较o1、o2的大小:如果该方法返回正整数,则表明o1大于o2;如果该方法返回0,则表明o1等于o2;如果该方法返回负整数,则表明o1小于o2。

  如下所示:如果需要实现定制排序(我们这实现倒序),则需要在创建TreeSet集合对象时,并提供一个Comparator对象与该TreeSet集合关联,由该Comparator对象负责集合元素的排序逻辑。

  1. class Person{
  2. Integer age;
  3. public Person(int age){
  4. this.age = age;
  5. }
  6. @Override
  7. public String toString() {
  8. return "Person [age=" + age + "]";
  9. }
  10. }
  11. public class Test {
  12. public static void main(String[] args){
  13. TreeSet<Person> persons = new TreeSet<Person>(new Comparator<Person>(){
  14. @Override
  15. public int compare(Person o1, Person o2) {
  16. if(o1.age > o2.age){
  17. return -1;
  18. }else if(o1.age == o2.age){
  19. return 0;
  20. }else{
  21. return 1;
  22. }
  23. }
  24. });
  25.  
  26. persons.add(new Person(2));
  27. persons.add(new Person(5));
  28. persons.add(new Person(6));
  29. //打印结果为[Person [age=6], Person [age=5], Person [age=2]]倒序
  30. System.out.println(persons);
  31. }
  32. }

  上面程序创建了一个Compartor接口的匿名内部类对象,该对象负责persons集合的排序。所以当我们把Person对象添加到persons集合中时,无须Person类实现Comparable接口,因为此时TreeSet无须通过Person对象来比较大小,而是由与TreeSet关联的Compartor对象来负责集合元素的排序。

  • EnumSet类

  EnumSet是一个专为枚举设计的集合类,EnumSet中所有值都必须是指定枚举类型的枚举值,该枚举类型在创建EnumSet时显式或隐性的指定。EnumSet的集合元素也是有序的,EnumSet以枚举值在Enum类内的定义顺序来决定集合元素的排序。

  EnumSet在内部以位向量的形式存储,这种存储形式非常紧凑、高效,因此EnumSet对象占用内存很小,而且运行效率很好。尤其是当进行批量操作(如调用containsAll和retainAll方法)时,如其参数也是EnumSet集合,则该批量操作的执行速度也非常快。

  EnumSet集合不容许加入null元素。如果试图插入null元素,EnumSet将会抛出NullPointerException异常。

  EnumSet类没有暴露任何构造器来创建该类的实例,程序应该通过它提供的static方法来创建EnumSet对象。它提供了如下常用static方法来创建EnumSet对象:

    1. static EnumSet allOf(Class elementType);   创建一个包含指定枚举类里所有枚举值的EnumSet集合。
    2. static EnumSet complementOf(EnumSet s); 创建一个其元素类型与指定EnumSet里元素类型相同的EnumSet,新EnumSet集合包含原EnumSet所不包含的、此枚举类剩下的枚举值(有点绕,看下面的例子,一看就懂)。
    3. static EnumSet copyOf(Collection c);  使用一个普通集合来创建EnumSet集合。
    4. static EnumSet copyOf(EnumSet s);   创建一个与指定EnumSet具有相同元素集合类型、相同集合元素的EnumSet。
    5. static EnumSet noneOf(Class elementType); 创建一个集合类型为指定枚举类型的空EnumSet。
    6. static EnumSet of(E first, E...rest);  创建一个包含一个或多个枚举值的EnumSet,传入的多个枚举值必须属于同一个枚举类。
    7. static EnumSet range(E first, E to);  创建包含从from枚举值,到to枚举值范围内所有枚举值的EnumSet集合。
  1. enum Season{
  2. SPRING,SUMMER,AUTUMN,WINTER
  3. }
  4. public class Test {
  5. public static void main(String[] args){
  6. //1.0创建一个EnumSet集合,集合元素就是Season枚举类的全部枚举值
  7. EnumSet<Season> es = EnumSet.allOf(Season.class);
  8. System.out.println(es);//输出[SPRING, SUMMER, AUTUMN, WINTER]
  9.  
  10. //2.0创建一个EnumSet空集合,指定其集合元素时Season类的枚举值。
  11. EnumSet<Season> es2 = EnumSet.noneOf(Season.class);
  12. System.out.println(es2);//输出[]
  13. //2.1手动添加两个元素
  14. es2.add(Season.AUTUMN);
  15. es2.add(Season.WINTER);
  16. System.out.println(es2);//输出[AUTUMN, WINTER]
  17.  
  18. //3.0以指定枚举值创建EnumSet集合
  19. EnumSet<Season> es3 = EnumSet.of(Season.SPRING, Season.SUMMER);
  20. System.out.println(es3);//输出[SPRING, SUMMER]
  21.  
  22. //4.0创建包含从Season.SPRING枚举值,到Season.AUTUMN枚举值范围内所有枚举值的EnumSet集合。
  23. EnumSet<Season> es4 = EnumSet.range(Season.SPRING, Season.AUTUMN);
  24. System.out.println(es4); //输出[SPRING, SUMMER, AUTUMN]
  25.  
  26. //5.0新创建的EnumSet集合元素和es4集合的元素有相同类型,es5的集合元素 + es4的集合元素 = Season 的所有枚举值
  27. EnumSet<Season> es5 = EnumSet.complementOf(es4);
  28. System.out.println(es5); //输出[WINNER]
  29.  
  30. //6.0复制Collection集合中所有元素来创建EnumSet集合。
  31. Collection<Season> c = new HashSet<Season>();
  32. c.add(Season.AUTUMN);
  33. c.add(Season.WINTER);
  34. EnumSet<Season> es6 = EnumSet.copyOf(c);
  35. System.out.println(es6); //输出[AUTUMN, WINTER]
  36. }
  37. }
  • 总结

  1. HashSet和TreeSet是Set的两个典型实现,HashSet的性能总是比TreeSet好(特别是比较常用的添加、查询元素等操作),因为TreeSet需要额外的红黑树算法来维护集合元素的次序。只有当需要一个保持排序的Set时,才应该使用TreeSet,否则都应该使用HashSet。
  2. HashSet还有一个子类:LinkedHashSet,对于普通插入、删除操作,LinkedHashSet比HashSet要略微慢一点;这是由维护链表所带来的额外开销所赵成的,不过,因为有了链表,遍历LinkedHashSet会更快。
  3. EnumSet是所有Set实现类中性能最好的,但它只能保存同一个枚举类的枚举值做为集合元素。
  4. Set的三个实现类HashSet(包括LinkedHashSet)、TreeSet和EnumSet都是线程不安全的。如果有多个线程同时访问一个Set集合,并且有超过一条线程修改了该Set集合,则必须手动保证该Set集合的同步性。通常可以通过Collections工具类的synchronizedSet方法来"包装"该Set集合。此操作最好在创建时进行,以防止对Set集合的意外非同步访问。例如:Set hs = Collections.synchronizedSet(new HashSet());

Java中的集合HashSet、LinkedHashSet、TreeSet和EnumSet(二)的更多相关文章

  1. Java容器深入浅出之HashSet、TreeSet和EnumSet

    Java集合中的Set接口,定义的是一类无顺序的.不可重复的对象集合.如果尝试添加相同的元素,add()方法会返回false,同时添加失败.Set接口包括3个主要的实现类:HashSet.TreeSe ...

  2. JAVA中Set集合--HashSet的使用

    一.使用HashSet添加一个String类型的值: public static void hashSet1(){ HashSet<String> hashSet = new HashSe ...

  3. 30、Java中Set集合之HashSet、TreeSet和EnumSet

    Set集合是Collection的子集,Set集合与Collection基本相同,没有提供任何额外的方法,只是Set不允许包含重复的元素. Set集合3个实现类:HashSet.TreeSet.Enu ...

  4. java集合框架之几种set(HashSet LinkedHashSet TreeSet )

    参考http://how2j.cn/k/collection/collection-sets/691.html#nowhere HashSet LinkedHashSet TreeSet HashSe ...

  5. java中的集合操作类(未完待续)

    申明: 实习生的肤浅理解,如发现有错误之处.还望大牛们多多指点 废话 事实上我写java的后台操作,我每次都会遇到一条语句:List<XXXXX> list = new ArrayList ...

  6. JAVA中的集合容器操作类

    目录 JAVA中的集合容器操作类 List集合 ArrayList的操作方法说明 LinkedList Stack Set Map Queue 总结 JAVA中的集合容器操作类 Java容器类库总共分 ...

  7. Java中的集合框架-Collection(二)

    上一篇<Java中的集合框架-Collection(一)>把Java集合框架中的Collection与List及其常用实现类的功能大致记录了一下,本篇接着记录Collection的另一个子 ...

  8. 分享知识-快乐自己:Java中各种集合特点

    Java中各种集合特点: Collection[单列集合]: List(有序,可重复): ArrayList: 底层数据结构是数组,查询快,增删慢.线程不安全,效率高. Vector: 底层数据结构是 ...

  9. Java中的集合(十一) 实现Map接口的TreeMap

    Java中的集合(十一) 实现Map接口的TreeMap 一.TreeMap简介(基于JDK1.8) TreeMap是基于红黑树数据结构,是一个key-value的有序集合,该映射根据其键的自然顺序进 ...

随机推荐

  1. kafka consumer 的配置(五)

    fetch.min.bytes. #获取最小字节数据 Consumer 向broker中要数据时是按大小来返回的,如果数据没有达到指定的MB,consumer会处于等待状态,直到broker 从pro ...

  2. #【Python】【demo实验23】【练习实例】【 三人比赛顺序问题 】

    原题: 两个乒乓球队进行比赛,各出三人.甲队为a,b,c三人,乙队为x,y,z三人.已抽签决定比赛名单.有人向队员打听比赛的名单.a说他不和x比,c说他不和x,z比,请编程序找出三队赛手的名单. 我的 ...

  3. H3C路由器登录策略专题讲解

    password-control login-attempt login-times [ exceed { lock | lock-time time | unlock } ] undo passwo ...

  4. PowerShell使用Clear-Content命令删除、清空文件内容的例子

    本文介绍PowerShell中如何将文件的内容进行删除或清空,使用VBScript要先创建FSO,然后再打开文件,再修改文件:PowerShell清空文件只要一句Clear-Content. 所谓清空 ...

  5. (八)动态 sql

    目录 什么是动态 sql sql 片段 foreach 标签 什么是动态 sql 我们之前在映射文件中,配置 sql 的时候,其实都是静态的 : <!--复杂查询--> <selec ...

  6. Java String类源码

    String类的签名(JDK 8): public final class String implements java.io.Serializable, Comparable<String&g ...

  7. Spring Boot(一) 初步理解Spring Boot

    一.Spring Boot所解决的问题 Java开发十分笨重:繁多的配置.低下的开发效率.复杂的部署流程以头疼的第三方技术集成. Spring Boot的理念:习惯优于配置——项目中存在大量的配置,此 ...

  8. 数据库分库分表策略之MS-SQL读写分离方案

    MS-SQL读写分离将从以下知识点进行展开: 以下截图内容来自博主:https://www.cnblogs.com/echosong/p/3603270.html 1.本地发布(写库如:centerd ...

  9. Python集合的常用方法

    注释很详细 collection={,,"apple","orange","cat"} # 查看一个数是否在集合中 in print(&qu ...

  10. request.getScheme() 使用方法(转)

    今天在看代码时,发现程序使用了 request.getScheme() .不明白是什么意思,查了一下.结果整理如下: 1.request.getScheme() 返回当前链接使用的协议:一般应用返回h ...