• java集合之Set接口


      Set集合通常不能记住元素添加的顺序,其他的操作和它的父接口基本相同。只是行为上有细微的差别,Set集合不能包含相同的元素。如果尝试添加相同的元素,调用add()方法将返回false,且新元素不能被加入容器。

    下面是Set接口的三个实现类,将一一介绍:

    1. HashSet

    HashSet 类具备以下特征:

    1. 不能保证元素的排列顺序,可能和添加的顺序不相同,也有可能会发生变化。
    2. HashSet不是线程安全的,如果多个线程同时访问HashSet,假设有两个以上的线程修改了HashSet集合时,在程序中必须要做同步处理。
    3. 集合元素值可以是null。

    在源码中,HashSet的实现调用HashMap的

    public class HashSet<E>
        extends AbstractSet<E>
        implements Set<E>, Cloneable, java.io.Serializable
    {
        static final long serialVersionUID = -5024744406713321676L;
    
        private transient HashMap<E,Object> map;
    
        // Dummy value to associate with an Object in the backing Map
        private static final Object PRESENT = new Object();
        
        public HashSet() {
            map = new HashMap<>();
        }
        ... //省略其他源码
    }

      也就是说,当想HashSet中存入一个对象时,将调用该对象的hashCode()方法来的到该对象的hashCode值,然后根据hashCode值来得到该对象在HashSet中的位置。如果两个对象的hashCode值不同,就算是同一个对象,HashSet也会将他们存放在不同的位置,也就是说,HashSet集合判断两个元素相等的标准是两个对象通过调用equals()方法返回true,并且两个对象的hashCode()方法返回值也相等。因此,当我们在把自定义的对象放入HashSet的时候,都需要重写这个对象发equals()方法和hashCode()方法,重写hashCode方法的规则如下:

    1. 在程序运行中,同一个对象多次调用hashCode()方法的返回是一致的。
    2. 当两个对象的equals()方法返回为true时,hashCode()方法的返回值也应该相等。
    3. equals()方法中用作比较标准的实例变量,都应该用于计算hashCode值。

    2. LinkedHashSet

      LinkedHashSet是HashSet的子类,它也是根据元素的hashCode值来计算元素的存储位置的,并且同时使用链表来维护元素的次序,这样使得元素看起来像是有序的(插入顺序),当我们遍历LinkedHashSet中的元素时,将按照元素的插入顺序来访问集合中的元素。
      LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet,但是迭代Set中的所有的元素时,会有较好的性能,因为它是以链表来维护元素的顺序的。

    3. TreeSet

    • TreeSet是SortedSet接口的实现类,如名所示,TreeSet中的元素时有序的(大小序,而不是插入序)。

    • 与HashSet集合采用hashCode算法来决定元素的存储位置不同,TreeSet采用的是红黑树的数据结构来存储集合元素。但是TreeSet集合中的元素时按照上面规则来排序的呢?目前主持两种排序方式:

      1. 自然排序:加入TreeSet中的对象都实现Comparable接口,重写compareTo(Object obj)方法。同时需要保证加入TreeSet的而元素时同一类对象。
      2. 定制排序:加入TreeSet中的对象都实现Comparator接口,重写compare(T o1,T o2)方法。

    4. EnumSet

    • EnumSet是一个专为枚举类型设计的集合类,EnumSet中的所有元素都必须是指定枚举类型的枚举值,该枚举类型在创建EnumSet枚举类型的时候显式或者隐式的指定。EnumSet集合的元素也是有序的,顺序是枚举定义的类型。
    • EnumSet的内部以向量的形式存储,这样占用的空间小,效率高。EnumSet中不允许加入null值。

    总结:

    各Set实现类的性能分析

    1. HashSet和TreeSet是Set接口的两个典型的实现,如何选择呢?HashSet的性能总是比TreeSet好(特别是频繁的添加,查询操作),因为TreeSet需要额外的红黑树算法来维护集合的次序,只有需要集合有序的时候才使用TreeSet,否则都使用HashSet。
    2. HashSet 和其子类LinkedHashSet,对于普通的插入,删除操作,LinkedHashSet比HashSet要慢一些,因为LinkedHashSet需要维护链表额外开销,但是当需要遍历整个容器时,LinkedHashSet会更快。
    3. EnumSet是这几个实现当中性能最好的,但是EnumSet集合中只能存放枚举类的枚举值。

      注:Set接口中的所有实现HashSet、TreeSet、EnumSet、LinkedHashSet都不是线程安全的,当有两个以上线程同时修改时,必须在程序中手动实现同步性(通常可以使用Collections工具类中的synchronizedSortedSet方法来包装Set集合,最好是在创建的时候设置)。

  • 相关阅读:
    如何将数组初始化为全0?
    什么是优先级队列(priority queue)?
    C语言中指针的指针是如何工作的?
    什么是队列(Queue)?
    理解*ptr++
    【Luogu】P4172水管局长(LCT)
    【Luogu】P4159迷路(矩阵优化)
    【Luogu】P3971Alice And Bob(贪心)
    【Luogu】P3211XOR和路径(高斯消元)
    【Luogu】P2445动物园(最大流)
  • 原文地址:https://www.cnblogs.com/ytuan996/p/10573606.html
Copyright © 2020-2023  润新知