13分鐘瞭解Java容器

List

Vector

採用Object數組存儲元素node

protected Object[] elementData;

Stack

Stack繼承了Vector,採用Object數組存儲元素數組

ArrayList

顧名思義,ArrayList底層採用數組存儲元素數據結構

Object[] elementData;

LinkedList

顧名思義,LinkedList底層採用鏈表存儲元素。經過firstlast指針分別指向頭元素和尾元素。併發

/**
 * Pointer to first node.
 * Invariant: (first == null && last == null) ||
 *            (first.prev == null && first.item != null)
 */
transient Node<E> first;
/**
 * Pointer to last node.
 * Invariant: (first == null && last == null) ||
 *            (last.next == null && last.item != null)
 */
transient Node<E> last;

其中NodeLinkedList自定義的靜態類,經過nextprev指針分別指向後驅節點和前驅節點。所以LinkedList底層是經過firstlast指針分別指向頭元素和尾元素的雙向鏈表函數

private static class Node<E> {
    E item;
    Node<E> next;
    Node<E> prev;
    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

CopyOnWriteArrayList

顧名思義,CopyOnWriteArrayList底層是經過Object[] 數組存儲數據,提供的構造方法在初始化時自動建立了長度爲0的數組。ui

/** The array, accessed only via getArray/setArray. */
private transient volatile Object[] array;
/**
 * Creates an empty list.
 */
public CopyOnWriteArrayList() {
    setArray(new Object[0]);
}

CopyOnWrite的邏輯大致上都是在插入數據的時候,從新複製一份數組,在新數組中插入新數據,以後將指針指向新的數組。這種模式主要是爲了針對「改動操做不多,主要是讀操做」的業務。CopyOnWriteArrayList同樣,在操做數據時經過ReentrantLock鎖住防止併發以後,複製原數組再在新數組中操做數據,最後將指針指向新數組。this

  • 相關代碼
public E set(int index, E element) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        E oldValue = get(elements, index);

        if (oldValue != element) {
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len);
            newElements[index] = element;
            setArray(newElements);
        } else {
            // Not quite a no-op; ensures volatile write semantics
            setArray(elements);
        }
        return oldValue;
    } finally {
        lock.unlock();
    }
}

public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        int len = elements.length;
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}

public E remove(int index) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        int len = elements.length;
        E oldValue = get(elements, index);
        int numMoved = len - index - 1;
        if (numMoved == 0)
            setArray(Arrays.copyOf(elements, len - 1));
        else {
            Object[] newElements = new Object[len - 1];
            System.arraycopy(elements, 0, newElements, 0, index);
            System.arraycopy(elements, index + 1, newElements, index,
                             numMoved);
            setArray(newElements);
        }
        return oldValue;
    } finally {
        lock.unlock();
    }
}

Map

HashTable

HashTable底層經過Entry數組存儲元素,spa

private transient Entry<?,?>[] table;
  • 其中Entry類型爲HashTable自定義的數據結構,內部記錄了元素的hash值,keyvalue,以及下一節點的指針next
private static class Entry<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    Entry<K,V> next;
    protected Entry(int hash, K key, V value, Entry<K,V> next) {
        this.hash = hash;
        this.key =  key;
        this.value = value;
        this.next = next;
    }
    @SuppressWarnings("unchecked")
    protected Object clone() {
        return new Entry<>(hash, key, value,
                              (next==null ? null : (Entry<K,V>) next.clone()));
    }
    // Map.Entry Ops
    public K getKey() {
        return key;
    }
    public V getValue() {
        return value;
    }
    public V setValue(V value) {
        if (value == null)
            throw new NullPointerException();
        V oldValue = this.value;
        this.value = value;
        return oldValue;
    }
    public boolean equals(Object o) {
        if (!(o instanceof Map.Entry))
            return false;
        Map.Entry<?,?> e = (Map.Entry<?,?>)o;
        return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
           (value==null ? e.getValue()==null : value.equals(e.getValue()));
    }
    public int hashCode() {
        return hash ^ Objects.hashCode(value);
    }
    public String toString() {
        return key.toString()+"="+value.toString();
    }
}

HashMap

HashMap底層經過 Node數組存儲元素線程

transient Node<K,V>[] table;
  • 其中NodeHashMap自定義的數據結構,內部記錄了元素的hash值,keyvalue,以及下一節點的指針next
static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    Node<K,V> next;
    Node(int hash, K key, V value, Node<K,V> next) {
        this.hash = hash;
        this.key = key;
        this.value = value;
        this.next = next;
    }
    public final K getKey()        { return key; }
    public final V getValue()      { return value; }
    public final String toString() { return key + "=" + value; }
    public final int hashCode() {
        return Objects.hashCode(key) ^ Objects.hashCode(value);
    }
    public final V setValue(V newValue) {
        V oldValue = value;
        value = newValue;
        return oldValue;
    }
    public final boolean equals(Object o) {
        if (o == this)
            return true;
        if (o instanceof Map.Entry) {
            Map.Entry<?,?> e = (Map.Entry<?,?>)o;
            if (Objects.equals(key, e.getKey()) &&
                Objects.equals(value, e.getValue()))
                return true;
        }
        return false;
    }
}

當元素hash相同時,元素會經過鏈表的形式經過next指針相連。在JDK1.8以後,當知足必定條件後,鏈表會轉化爲紅黑樹,此時,元素會從Node類型轉化爲TreeNode類型。3d

  • TreeNodeHashMap自定義的數據結構,內部記錄父節點parent,左右孩子leftright,顏色標誌red已經同級前驅節點prev
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
    TreeNode<K,V> parent;  // red-black tree links
    TreeNode<K,V> left;
    TreeNode<K,V> right;
    TreeNode<K,V> prev;    // needed to unlink next upon deletion
    boolean red;
    TreeNode(int hash, K key, V val, Node<K,V> next) {
        super(hash, key, val, next);
    }
    ......
 }

具體的操做邏輯能夠參考:[[HashMap]]

LinkedHashMap

LinkedHashMap繼承了HashMap的邏輯,只不過添加了Entry結構。以及headtail首尾指針。

static class Entry<K,V> extends HashMap.Node<K,V> {
    Entry<K,V> before, after;
    Entry(int hash, K key, V value, Node<K,V> next) {
        super(hash, key, value, next);
    }
}
private static final long serialVersionUID = 3801124242820219131L;
/**
 * The head (eldest) of the doubly linked list.
 */
transient LinkedHashMap.Entry<K,V> head;
/**
 * The tail (youngest) of the doubly linked list.
 */
transient LinkedHashMap.Entry<K,V> tail;

Entry中保存了beforeafter指針,指向上一個插入元素Entry以及後一個插入元素Entry。以數組+紅黑樹+元素鏈表+插入順序鏈表的形式存儲數據。

ConcurrentHashMap

ConcurrentHashMap底層經過Node數組存儲數據

transient volatile Node<K,V>[] table;
  • 其中NodeConcurrentHashMap自定義的數據結構,內部記錄了元素的hash值,keyvalue,以及下一節點的指針next
static class Node<K,V> implements Map.Entry<K,V> {
      final int hash;
      final K key;
      volatile V val;
      volatile Node<K,V> next;
      Node(int hash, K key, V val, Node<K,V> next) {
          this.hash = hash;
          this.key = key;
          this.val = val;
          this.next = next;
      }
      public final K getKey()       { return key; }
      public final V getValue()     { return val; }
      public final int hashCode()   { return key.hashCode() ^ val.hashCode(); }
      public final String toString(){ return key + "=" + val; }
      public final V setValue(V value) {
          throw new UnsupportedOperationException();
      }
      public final boolean equals(Object o) {
          Object k, v, u; Map.Entry<?,?> e;
          return ((o instanceof Map.Entry) &&
                  (k = (e = (Map.Entry<?,?>)o).getKey()) != null &&
                  (v = e.getValue()) != null &&
                  (k == key || k.equals(key)) &&
                  (v == (u = val) || v.equals(u)));
      }
      /**
       * Virtualized support for map.get(); overridden in subclasses.
       */
      Node<K,V> find(int h, Object k) {
          Node<K,V> e = this;
          if (k != null) {
              do {
                  K ek;
                  if (e.hash == h &&
                      ((ek = e.key) == k || (ek != null && k.equals(ek))))
                      return e;
              } while ((e = e.next) != null);
          }
          return null;
      }
  }

當元素hash相同時,元素會經過鏈表的形式經過next指針相連。在JDK1.8以後,當知足必定條件後,鏈表會轉化爲紅黑樹,此時,table數組中的Node 元素會轉化爲TreeBin類型,TreeBin類型記錄了由TreeNode構成紅黑樹的根節點root。

  • TreeBinConcurrentHashMap自定義,繼承了Node類型。存放了下轄紅黑樹的根節點root
static final class TreeBin<K,V> extends Node<K,V> {
    TreeNode<K,V> root;
    volatile TreeNode<K,V> first;
    volatile Thread waiter;
    volatile int lockState;
    // values for lockState
    static final int WRITER = 1; // set while holding write lock
    static final int WAITER = 2; // set when waiting for write lock
    static final int READER = 4; // increment value for setting read lock
    ......
}
  • TreeNodeConcurrentHashMap自定義,跟HashMapTreeNode相似,可是ConcurrentHashMapTreeNode繼承自Node類型。
static final class TreeNode<K,V> extends Node<K,V> {
    TreeNode<K,V> parent;  // red-black tree links
    TreeNode<K,V> left;
    TreeNode<K,V> right;
    TreeNode<K,V> prev;    // needed to unlink next upon deletion
    boolean red;
    TreeNode(int hash, K key, V val, Node<K,V> next,
             TreeNode<K,V> parent) {
        super(hash, key, val, next);
        this.parent = parent;
    }
    ......
}

具體的操做邏輯能夠參考:[[ConcurrentHashMap]]

TreeMap

TreeMap 經過Entry存儲數據,底層是以紅黑樹的邏輯結構存儲。

private transient Entry<K,V> root;
  • EntryTreeMap自定義的數據結構,其中包含keyvalue,左右子樹leftright,以及父節點parent,和標記顏色的color
static final class Entry<K,V> implements Map.Entry<K,V> {
      K key;
      V value;
      Entry<K,V> left;
      Entry<K,V> right;
      Entry<K,V> parent;
      boolean color = BLACK;
      /**
       * Make a new cell with given key, value, and parent, and with
       * {@code null} child links, and BLACK color.
       */
      Entry(K key, V value, Entry<K,V> parent) {
          this.key = key;
          this.value = value;
          this.parent = parent;
      }
      ......
}

WeakHashMap

WeakHashMap底層經過Entry[] 數組存儲數據,採用鏈表法解決hash衝突,跟HashMap 不一樣沒有進行長鏈表的紅黑樹轉化。

Entry<K,V>[] table;
  • EntryWeakHashMap自定義的數據結構繼承了WeakReference,其中包含valuehash值,以及next指針。key弱引用reference的值。
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
    V value;
    final int hash;
    Entry<K,V> next;
    /**
     * Creates new entry.
     */
    Entry(Object key, V value,
          ReferenceQueue<Object> queue,
          int hash, Entry<K,V> next) {
        super(key, queue);
        this.value = value;
        this.hash  = hash;
        this.next  = next;
    }
    ......
}

具體的操做邏輯能夠參考:[[WeakHashMap]]

ConcurrentSkipListMap

ConcurrentSkipListMap底層經過跳錶的數據結構提升查找效率。ConcurrentSkipListMap經過Node存儲基本的key,value數據,Index存儲跳錶索引,HeadIndex爲層級頭索引。

// Node存儲基本的key,value數據,是一個簡單的鏈表
static final class Node<K,V> {
    final K key;
    volatile Object value;
    volatile Node<K,V> next;
    ...
}
// Index 內包含Node元素,以及向下和向右的Index指針
static class Index<K,V> {
    final Node<K,V> node;
    final Index<K,V> down;
    volatile Index<K,V> right;
    ...
} 
// HeadIndex繼承了Index,添加了level索引層級屬性
static final class HeadIndex<K,V> extends Index<K,V> {
    final int level;
    HeadIndex(Node<K,V> node, Index<K,V> down, Index<K,V> right, int level) {
        super(node, down, right);
        this.level = level;
    }
}

同時,ConcurrentSkipListMap經過對大部分操做添加CAS鎖防止併發。


Set

HashSet

HashSet底層默認經過HashMap存儲元素,數據儲存在HashMapkey中,value設置爲默認值Object

private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object(); // default value
public boolean add(E e) {
  return map.put(e, PRESENT)==null;
}

HashSet 底層還能夠經過 LinkedHashMap 存儲元素。根據構造函數參數不一樣選擇是 HashMap仍是LinkedHashMap實現。

HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

LinkedHashSet

LinkedhashSet繼承了HashSet,採用LinkedHashMap存儲元素。

ConcurrentSkipListSet

底層經過ConcurrentSkipListMap實現,存放的值做爲key存入ConcurrentSkipListMap中,使用Boolean.TRUE對象最爲默認的value

private final ConcurrentNavigableMap<E,Object> m;

/**
 * Constructs a new, empty set that orders its elements according to
 * their {@linkplain Comparable natural ordering}.
 */
public ConcurrentSkipListSet() {
    m = new ConcurrentSkipListMap<E,Object>();
}

public boolean add(E e) {
    return m.putIfAbsent(e, Boolean.TRUE) == null;
}

TreeSet

TreeSet底層經過TreeMap存儲元素,和HashSet相似,數據存儲在TreeMap Key中,value設置爲默認值Object

private transient NavigableMap<E,Object> m;
  // Dummy value to associate with an Object in the backing Map
  private static final Object PRESENT = new Object();
  
  public TreeSet(Comparator<? super E> comparator) {
      this(new TreeMap<>(comparator));
  }

CopyOnWriteArraySet

底層經過CopyOnWriteArrayList實現,經過List.addIfAbsent()方法實現同一個元素只存在一個,保證了元素的惟一性。

private final CopyOnWriteArrayList<E> al;

/**
 * Creates an empty set.
 */
public CopyOnWriteArraySet() {
    al = new CopyOnWriteArrayList<E>();
}

public boolean add(E e) {
    return al.addIfAbsent(e);
}

Queue

PriorityQueue

PriorityQueue優先隊列,底層經過Object數組存儲元素,Object[]以層次遍歷的形式存儲優先隊列(徹底二叉樹)的元素值。索引n的左右孩子索引分別爲$2n+1,2*(n+1)$;

transient Object[] queue;

BlockingQueue

A Queue that additionally supports operations that wait for the queue to become non-empty when retrieving an element, and wait for space to become available in the queue when storing an element.

阻塞隊列接口,提供了額外的一些操做,當這些操做沒法當即執行時(插入時容量滿了、刪除/獲取元素時隊列爲空),不會像正常的queue一些當即返回而是阻塞一段時間直到知足條件(設置了等待時間、條件知足了)

阻塞隊列的阻塞操做通常是經過建立多個條件鎖ReentrantLock.Condition實現的,經過調用Condition.await()以及Condition.signal()進行線程的阻塞和喚醒

private final Condition notEmpty;

/** Condition for waiting puts */
private final Condition notFull;
  • 添加元素

    • add(E e)/offer(E e)方法,當數組滿了以後,直接返回false

      public boolean offer(E e) {
          checkNotNull(e);
          final ReentrantLock lock = this.lock;
          lock.lock();
          try {
              if (count == items.length)
                  return false;
              else {
                  enqueue(e);
                  return true;
              }
          } finally {
              lock.unlock();
          }
      }
    • put(E e)/offer(E e, long timeout, TimeUnit unit)方法,當數組滿了以後,阻塞直到喚醒或者阻塞指定時間

      public void put(E e) throws InterruptedException {
          checkNotNull(e);
          final ReentrantLock lock = this.lock;
          lock.lockInterruptibly();
          try {
              while (count == items.length)
                  notFull.await();
              enqueue(e);
          } finally {
              lock.unlock();
          }
      }
      
      public boolean offer(E e, long timeout, TimeUnit unit)
          throws InterruptedException {
      
          checkNotNull(e);
          long nanos = unit.toNanos(timeout);
          final ReentrantLock lock = this.lock;
          lock.lockInterruptibly();
          try {
              while (count == items.length) {
                  if (nanos <= 0)
                      return false;
                  nanos = notFull.awaitNanos(nanos);
              }
              enqueue(e);
              return true;
          } finally {
              lock.unlock();
          }
      }
  • 獲取元素

    • poll(),彈出隊首元素,隊列爲空時直接return null;
    • take() ,彈出隊首元素,隊列爲空時阻塞至隊列不爲空
    • poll(long timeout, TimeUnit unit),彈出隊首元素,隊列爲空時阻塞指定時間後重試一次。
    • peek()獲取隊首元素,隊列爲空時直接return null;

ArrayBlockingQueue

A bounded BlockingQueue backed by an array. This queue orders elements FIFO (first-in-first-out). The head of the queue is that element that has been on the queue the longest time. The tail of the queue is that element that has been on the queue the shortest time. New elements are inserted at the tail of the queue, and the queue retrieval operations obtain elements at the head of the queue.

顧名思義,ArrayBlockingQueue底層是經過Object[]數組實現元素的存儲的。ArrayBlockingQueue有界隊列,存儲元素的數量經過初始化方法指定。當數組滿了的時候,就沒法直接往數組中添加元素了。ArrayBlockingQueue FIFO,添加元素到隊尾,刪除隊首元素。

  • 初始化方法

    public ArrayBlockingQueue(int capacity, boolean fair) {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        this.items = new Object[capacity];
        lock = new ReentrantLock(fair);
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
    }

LinkedBlockingQueue

根據初始化參數動態構造是否有界的阻塞隊列。底層經過Node結構實現的鏈表進行存儲。

  • Node結構

    static class Node<E> {
        E item;
    
        Node<E> next;
    
        Node(E x) { item = x; }
    }
  • 初始化方法

    public LinkedBlockingQueue() {
        // 不指定隊列容量,默認爲Integer的最大值
        this(Integer.MAX_VALUE);
    }
    
    public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node<E>(null);
    }

ArrayBlockingQueue LinkedBlockingQueue除了底層實現方式不一樣以外,ArrayBlockingQueue在進行數據操做時用的都是同一把鎖final ReentrantLock lock;,而LinkedBlockingQueue初始化了二把鎖插入和彈出操做使用不一樣的鎖。final ReentrantLock putLock; final ReentrantLock takeLock;

相關文章
相關標籤/搜索