• Set接口

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

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

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

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

  从上面程序中可以看出,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里的全部元素时,将有很好的性能,因为它以列表来维护内部顺序。

public class Test {public static void main(String[] args){LinkedHashSet books = new LinkedHashSet();books.add("语文");books.add("数学");books.add("英语");//删除语文books.remove("语文");//重新添加books.add("语文");//打印结果为[数学, 英语, 语文]
            System.out.println(books);}
}

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

  • 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的元素组成。
public class Test {public static void main(String[] args){TreeSet<Integer> nums = new TreeSet<Integer>();nums.add(3);nums.add(1);nums.add(5);nums.add(-9);//1.返回第一个元素Integer first = nums.first();//打印结果为-9
            System.out.println(first);//2.返回最后一个元素Integer last = nums.last();//打印结果为5
            System.out.println(last);//3.返回上一个Integer lower = nums.lower(2);//打印结果为1
            System.out.println(lower);//4.返回下一个Integer higher = nums.higher(2);//打印结果为3
            System.out.println(higher);//5.返回小于3的子集,不包含3SortedSet<Integer> headSet = nums.headSet(3);//打印结果[-9, 1]
            System.out.println(headSet);//6.返回大于等于3的子集,包含3SortedSet<Integer> tailSet = nums.tailSet(3);//打印结果[3, 5]
            System.out.println(tailSet);//7.打印整个集合结果为[-9, 1, 3, 5]
            System.out.println(nums);}
}

  根据上面程序的运行结果可看出,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异常。代码如下:

class Person{}
public class Test {public static void main(String[] args){TreeSet<Person> persons = new TreeSet<Person>();persons.add(new Person());    System.out.println(persons);    }
}

以上代码将会抛出:

  • 定制排序

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

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

class Person{Integer age;public Person(int age){this.age = age;}@Overridepublic String toString() {return "Person [age=" + age + "]";}
}
public class Test {public static void main(String[] args){TreeSet<Person> persons = new TreeSet<Person>(new Comparator<Person>(){@Overridepublic int compare(Person o1, Person o2) {if(o1.age > o2.age){return -1;}else if(o1.age == o2.age){return 0;}else{return 1;}}});persons.add(new Person(2));persons.add(new Person(5));persons.add(new Person(6));//打印结果为[Person [age=6], Person [age=5], Person [age=2]]倒序
        System.out.println(persons);}
}

  上面程序创建了一个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集合。
enum Season{SPRING,SUMMER,AUTUMN,WINTER
}
public class Test {public static void main(String[] args){//1.0创建一个EnumSet集合,集合元素就是Season枚举类的全部枚举值EnumSet<Season> es = EnumSet.allOf(Season.class);System.out.println(es);//输出[SPRING, SUMMER, AUTUMN, WINTER]//2.0创建一个EnumSet空集合,指定其集合元素时Season类的枚举值。EnumSet<Season> es2 = EnumSet.noneOf(Season.class);System.out.println(es2);//输出[]//2.1手动添加两个元素
        es2.add(Season.AUTUMN);es2.add(Season.WINTER);System.out.println(es2);//输出[AUTUMN, WINTER]//3.0以指定枚举值创建EnumSet集合EnumSet<Season> es3 = EnumSet.of(Season.SPRING, Season.SUMMER);System.out.println(es3);//输出[SPRING, SUMMER]//4.0创建包含从Season.SPRING枚举值,到Season.AUTUMN枚举值范围内所有枚举值的EnumSet集合。EnumSet<Season> es4 = EnumSet.range(Season.SPRING, Season.AUTUMN);System.out.println(es4); //输出[SPRING, SUMMER, AUTUMN]//5.0新创建的EnumSet集合元素和es4集合的元素有相同类型,es5的集合元素 + es4的集合元素 = Season 的所有枚举值EnumSet<Season> es5 = EnumSet.complementOf(es4);System.out.println(es5); //输出[WINNER]//6.0复制Collection集合中所有元素来创建EnumSet集合。Collection<Season> c = new HashSet<Season>();c.add(Season.AUTUMN);c.add(Season.WINTER);EnumSet<Season> es6 = EnumSet.copyOf(c);System.out.println(es6); //输出[AUTUMN, WINTER]
    }
}

  • 总结

  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());

转载于:https://www.cnblogs.com/be-forward-to-help-others/p/6708130.html

Java中的集合HashSet、LinkedHashSet、TreeSet和EnumSet(二)相关推荐

  1. Java—Set集合详解(HashSet/LinkedHashSet/TreeSet/EnumSet)

    关注微信公众号:CodingTechWork,一起学习进步. Set集合介绍 Set集合的概念   Set集合类似于一个容器,程序把很多对象保存到Set集合中,Set集合对添加顺序不记录,当有重复的对 ...

  2. 集合{LinkedHashMap TreeMap HashSet LinkedHashSet TreeSet 快速失败机制 ConcurrentHashMap CAS 多线程协同扩容}(二)

    目录标题 LinkedHashMap Map集合框架结构体系图 什么是LinkedHashMap Linked 链式 的意思 HashMap "哈希映射"的意思 LinkedHas ...

  3. 集合之Collection家族的 List接口+LinkedList+Vector+Stack及Set接口+HashSet+LinkedHashSet+TreeSet

    集合之Collection家族的 List接口+LinkedList+Vector+Stack及Set接口+HashSet+LinkedHashSet+TreeSet 一.LinkedList 1.L ...

  4. java中各种集合的用法和比较

    一,java中各种集合的关系图 Collection       接口的接口     对象的集合  ├ List           子接口         按进入先后有序保存   可重复  │├ L ...

  5. java中的集合_Java 集合介绍,常用集合类

    JAVA 集合 在处理数据的过程中经常会需要一个容器来存储某一类型的数据,Java 中的数组就是这样一种容器.但 Java 中的数组有其局限性,定义后的数组长度不可变,超出数组长度后就不能再存放数据了 ...

  6. 10.JAVA中的集合(数据结构)

    Java中的集合 包含以下结构: 数组-线性表 链表 栈 队列 散列表 二叉树 映射关系(key-value) List集合  特点:[有序.重复] [线性表--数组] ArrayList 定义 线程 ...

  7. 谈谈java中的集合框架

    集合框架中的接口. 哪些集合可以存放null? 除了TreeSet和TreeMap还有HashTable不能存放null,会报空指针异常. List的主要特点就是有序性和元素的可空性. ArrayLi ...

  8. java 中的集合_JAVA中的集合

    JAVA中集合,是java中的基础概念,今天接着跟讲课准备课件的机会,重新整理一下关于JAVA中的集合的概念. 集合的概念 java中的集合类是用来存放对象的 集合相当于一个容器,里面包容着一组对象- ...

  9. Java中的集合多线程的理解

    Java中的集合 1.List.Set和Queue [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DwpgIu9t-1587300088779)(file:///C:\ ...

  10. Java中的集合浅析

    java中的集合就像是一个容器一样,专门用来存储java对象的引用,这些对象可以是任意的数据类型并且长度可变,集合类位于java.util包中. java集合按照其存储结构可以分为两大类,一个是单列集 ...

最新文章

  1. Android系统的启动过程
  2. php爬取flash的交互数据库,基于PHP的Flash与MySQL数据库通讯的实现
  3. 说说我在制造业大厂当了一个月程序员的感受
  4. URL重写:RewriteCond指令与RewriteRule 指令格式
  5. rsync(一):基本命令和用法
  6. 步骤条自定义图片_用函数公式查找图片?我可没骗你
  7. luogu P2783 有机化学之神偶尔会做作弊
  8. PyCharm 2022 软件安装及汉化
  9. 操作系统原理与实践-1熟悉实验环境
  10. 我的世界如何安装java环境变量_JDK安装与环境变量配置方法
  11. Python 可轻松开发植物大战僵尸游戏(附动态演示+源码分享)
  12. Matlab 口罩识别
  13. 键盘按钮KeyCode使用案例
  14. as3.0点击获取TLF文本的实例名
  15. 29(将数字字符串转换成数字)
  16. 娱乐至死,年轻人的慢性毒药
  17. 地面分辨率,空间分辨率(GSD为地面采样间隔)
  18. 陌上花开缓缓归(转)
  19. psd转换为html模板
  20. css3 text-overflow制作固定区域的博客列表

热门文章

  1. windows 端口占用
  2. linux 时间服务器安装配置
  3. Chrome调试vue项目时session异常,后端tp6 session使用异常
  4. 结构体学习笔记6——结构体嵌套
  5. saltstack学习-1:saltstack介绍、部署、常见问题处理
  6. ASP.NET MVC 对于视图引擎的优化
  7. 昨天一冲动,给头儿说留所了。。。
  8. 将Java程序作成exe文件的几种方法
  9. 软开关设计漫谈_软件篇
  10. 【react】 react---项目的-----------简单路由配置