java集合-Set篇

java集合-Set篇

JDK提供的集合类型主要分为四种类型:

  1. List:支持重复元素
  2. Set:不支持重复元素
  3. Map:键/值对的映射集
  4. Queue/Deque(double ended queue):queue是在集合尾部添加元素,在头部删除元素的队列,deque是可在头部和尾部添加或者删除元素的双端队列,deque既可以实现队列又可以实现栈

本文基于JDK8,java version “1.8.0_251”

EnumSet(RegularEnumSet/JumboEnumSet)

枚举类的容器,非线程安全,有序集合

  1. EnumSet是抽象类,使用静态方法noneOf构建,如果枚举类的值不超过64,则使用创建RegularEnumSet实例,如果超过64位,则创建的JumboEnumSet。

    EnumSet.noneOf(E.class);
    
    public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
        Enum<?>[] universe = getUniverse(elementType);
        if (universe == null)
            throw new ClassCastException(elementType + " not an enum");
    
        if (universe.length <= 64)
            return new RegularEnumSet<>(elementType, universe);
        else
            return new JumboEnumSet<>(elementType, universe);
    }
    
  2. 有序集合,EnumSet以枚举值在Enum类内的定义顺序来决定集合元素的顺序

  3. RegularEnumSet内部通过Bit数组来存放枚举值,而这个Bit数组其实就是一个Long类型数值,初始时是0L,是一个容量为64的Bit数组(Long类型长度为8字节,一个字节等于8位)。添加元素时,是把对应枚举元素的ordinal(每一个枚举类的枚举值都对应一个ordinal值)值映射到64Bit上的某一个位置为1。

    /**
     * Bit vector representation of this set.  The 2^k bit indicates the
     * presence of universe[k] in this set.
     */
    private long elements = 0L;
    /**
     * Adds the specified element to this set if it is not already present.
     *
     * @param e element to be added to this set
     * @return true if the set changed as a result of the call
     *
     * @throws NullPointerException if e is null
     */
    public boolean add(E e) {
        typeCheck(e);
    
        long oldElements = elements;
        elements |= (1L << ((Enum<?>)e).ordinal());
        return elements != oldElements;
    }
    
  4. JumboEnumSet内部则通过long数组类存放枚举值

    /**
     * Bit vector representation of this set.  The ith bit of the jth
     * element of this array represents the  presence of universe[64*j +i]
     * in this set.
     */
    private long elements[];
    /**
     * Adds the specified element to this set if it is not already present.
     *
     * @param e element to be added to this set
     * @return true if the set changed as a result of the call
     *
     * @throws NullPointerException if e is null
     */
    public boolean add(E e) {
        typeCheck(e);
    
        int eOrdinal = e.ordinal();
        int eWordNum = eOrdinal >>> 6;
    
        long oldElements = elements[eWordNum];
        elements[eWordNum] |= (1L << eOrdinal);
        boolean result = (elements[eWordNum] != oldElements);
        if (result)
            size++;
        return result;
    }
    
  5. 非线程安全,可以通过**Collections.synchronizedSet()**方法把它转成线程安全的集合,即使性能不是很高,但是似乎是唯一的选择。

TreeSet

有序集合,非线程安全,不允许null元素(Treemap不允许null的key,因为要使用它的compareTo方法)

  1. 内部通过TreeMap来存储元素,把元素存储在TreeMap的key里,value为同一个Object实例,通过TreeMap存储Key的有序性和无重复性来实现自己的有序性和Set的的元素无重复性;插入元素时,会根据元素compareTo方法判断是否重复和顺序(所以TreeSet中的元素必须实现Comparable接口并重写compareTo方法),然后进行排序。

    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();
    /**
     * Constructs a new, empty tree set, sorted according to the
     * natural ordering of its elements.  All elements inserted into
     * the set must implement the {@link Comparable} interface.
     * Furthermore, all such elements must be mutually
     * comparable: {@code e1.compareTo(e2)} must not throw a
     * {@code ClassCastException} for any elements {@code e1} and
     * {@code e2} in the set.  If the user attempts to add an element
     * to the set that violates this constraint (for example, the user
     * attempts to add a string element to a set whose elements are
     * integers), the {@code add} call will throw a
     * {@code ClassCastException}.
     */
    public TreeSet() {
        this(new TreeMap<E,Object>());
    }
    
  2. 非线程安全,可以通过**Collections.synchronizedSortedSet()**方法把它转成线程安全的集合

  3. 不支持fail-fast机制,用的是TreeMap的iterator,没有checkForComodification(),更不会抛出ConcurrentModificationException异常。

  4. 不允许null元素,会抛出空指针异常,Treemap不允许null的key,插入时要使用它的compareTo方法会造成空指针异常

  5. 有序集合,根据元素的CompareTo方法顺序或自定义的comparator比较顺序。

HashSet

无序集合,非线程安全,允许null元素

  1. 内部通过HashMap来存储元素,把元素存储在HashMap的key里,value为同一个Object实例,通过HashMap存储Key的无重复性来实现自己的无重复性,插入元素时,会根据元素的hashcode方法判断元素是否重复(key为null时,hashcode为0),包含一个基于LinkedHashMap的构造方法(给同一个包下的其他类使用,比如LinkedHashSet)。

    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();
    public HashSet() {
        map = new HashMap<>();
    }
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
    
  2. 非线程安全,可以通过**Collections.synchronizedSortedSet()**方法把它转成线程安全的集合

  3. 支持fail-fast机制,用的是HashMap的iterator,会判断modCount的值是否被修改,如果modCount != expectedModCount,则抛出ConcurrentModificationException异常。

  4. 允许null元素,因为HashMap允许null的key

  5. 无序集合

LinkedHashSet

有序集合,非线程安全,允许null元素

  1. 继承至HashSet,除了构造方法和重写的spliterator方法,没有任何多余的成员变量和其他的方法。

    public class LinkedHashSet<E>
        extends HashSet<E>
        implements Set<E>, Cloneable, java.io.Serializable {
    
  2. 有序集合,内部通过LinkedHashMap来存储元素,构造方法中主要是调用父类HashSet的构造方法构建一个LinkedHashMap,相比LinkedHashMap可以控制按插入元素时的顺序还是按访问顺序进行排序,LinkedHashSet只能按插入元素的顺序进行排序

  3. 非线程安全,可以通过**Collections.synchronizedSortedSet()**方法把它转成线程安全的集合。

  4. 支持fail-fast机制,nextNode方法中会判断modCount值,如果modCount != expectedModCount,则会抛出ConcurrentModificationException异常。

  5. 允许null元素,因为LinkedHashMap允许null的key

CopyOnWriteArraySet

无序集合,基于CopyOnWriteArrayList,线程安全,适合set大小一般很小且多读场景,内存占用大

  1. 基于CopyOnWriteArrayList实现,所有的操作都是通过CopyOnWriteArrayList对象进行的,构造方法中直接创建一个CopyOnWriteArrayList对象。

    public class CopyOnWriteArraySet<E> extends AbstractSet<E>
            implements java.io.Serializable {
        private static final long serialVersionUID = 5457747651344034263L;
        private final CopyOnWriteArrayList<E> al;
        public CopyOnWriteArraySet() {
            al = new CopyOnWriteArrayList<E>();
        }
    
  2. 添加元素前需要遍历整个数组,因为不允许重复,所以添加元素之前需要判断元素是否存在,CopyOnWriteArrayList额外提供了addIfAbsent()和addAllAbsent()这两个添加元素的API,通过这些API来添加元素时,只有当元素不存在时才执行添加操作。

  3. 线程安全。和CopyOnWriteArrayList一样,通过volatile和互斥锁来实现(因为直接使用了CopyOnWriteArrayList的方法)。

  4. 不支持fail-fast机制

ConcurrentSkipListSet

有序集合,基于ConcurrentSkipListMap,线程安全,适用于高并发场景

  1. 基于ConcurrentSkipListMap,把元素存储在ConcurrentSkipListMap的key里,value为常量Boolean.TRUE,通过ConcurrentSkipListMap存储Key的有序性和无重复性来实现自己的有序性和Set的的元素无重复性

    public class ConcurrentSkipListSet<E>
        extends AbstractSet<E>
        implements NavigableSet<E>, Cloneable, java.io.Serializable {
    
        private final ConcurrentNavigableMap<E,Object> m;//实际上ConcurrentSkipListMap的实例
    
        public ConcurrentSkipListSet() {
            m = new ConcurrentSkipListMap<E,Object>();
        }
    
  2. 线程安全,因为ConcurrentSkipListMap是线程安全的。

  3. 不支持fail-fast机制,用的是ConcurrentSkipListMap的KeyIterator。

  4. 不允许null元素,会抛出空指针异常。ConcurrentSkipListMap的key和value都不支持null。

  5. 有序集合,根据元素的CompareTo方法顺序或自定义的comparator比较顺序。

你可能感兴趣的:(JAVA,队列,java,数据结构)