數組,線程不安全。前端
/*minCapacity爲原list長度*/ private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); } private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }
默認狀況1.5倍增加java
數組,線程安全(條件)算法
public Object deleteLast(Vector v){ int lastIndex = v.size()-1; v.remove(lastIndex); }
執行deleteLast
操做時若是不加鎖,可能會出現remove時size錯誤數組
繼承Vector
經過push、pop進行入棧,出棧安全
雙向鏈表,線程不安全數據結構
HashMap, 線程不安全併發
LinkedHashMap,線程不安全函數
public class LinkedHashSet<E> extends HashSet<E> implements Set<E>, Cloneable, java.io.Serializable { ... public LinkedHashSet() {super(16, .75f, true);} } //hashset.java中的構造方法 HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<>(initialCapacity, loadFactor); }
TreeMap, 線程不安全性能
queue使用時要儘可能避免Collection的add()和remove()方法,而是要使用offer()來加入元素,使用poll()來獲取並移出元素。它們的優
點是經過返回值能夠判斷成功與否,add()和remove()方法在失敗的時候會拋出異常。 若是要使用前端而不移出該元素,使用
element()或者peek()方法。this
一、沒有實現的阻塞接口的LinkedList:
實現了java.util.Queue接口和java.util.AbstractQueue接口
內置的不阻塞隊列: PriorityQueue 和 ConcurrentLinkedQueue
PriorityQueue 和 ConcurrentLinkedQueue 類在 Collection Framework 中加入兩個具體集合實現。
PriorityQueue 類實質上維護了一個有序列表。加入到 Queue 中的元素根據它們的自然排序(經過其 java.util.Comparable 實現)或者根據傳遞給構造函數的 java.util.Comparator 實現來定位。
ConcurrentLinkedQueue 是基於連接節點的、線程安全的隊列。併發訪問不須要同步。由於它在隊列的尾部添加元素並從頭部刪除它們,因此只要不須要知道隊列的大小, ConcurrentLinkedQueue 對公共集合的共享訪問就能夠工做得很好。收集關於隊列大小的信息會很慢,須要遍歷隊列。
2)實現阻塞接口的
java.util.concurrent 中加入了 BlockingQueue 接口和五個阻塞隊列類。它實質上就是一種帶有一點扭曲的 FIFO 數據結構。不是當即從隊列中添加或者刪除元素,線程執行操做阻塞,直到有空間或者元素可用。
五個隊列所提供的各有不一樣:
鏈表結構,Node結構
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; }
JDK 1.8新特性,當節點數大於8時,將鏈表轉換爲紅黑樹
static final int TREEIFY_THRESHOLD = 8; if (binCount >= TREEIFY_THRESHOLD - 1) treeifyBin(tab, hash);
過長的鏈表搜索性能下降,使用紅黑樹來提升查找性能。
static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
對key的hash碼高16位實現異或
a⊕b = (¬a ∧ b) ∨ (a ∧¬b)
若是a、b兩個值不相同,則異或結果爲1。若是a、b兩個值相同,異或結果爲0
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); ...
插入table時,下標index = (n - 1) & hash
在n較小時,hash碼的低位的0和1不均勻,容易衝突致使碰撞。而經過上述XOR算法調整後,hash的低16位會變大,從而使得0和1分佈更加均勻。
static final int MAXIMUM_CAPACITY = 1 << 30; public HashMap(int initialCapacity, float loadFactor) { /**省略此處代碼**/ this.loadFactor = loadFactor; this.threshold = tableSizeFor(initialCapacity); } static final int tableSizeFor(int cap) { int n = cap - 1; n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16; return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
假設cap=37,則n=36, 100100
結果爲63,二進制或操做只有在兩數都爲0時,才爲0,所以經過循環右移,或操做,實際是爲了找到n的最高位,並將後面的數字所有全改寫爲1,從而實現返回大於等於initialCapacity的最小的2的冪。
final Node<K,V>[] resize() { Node<K,V>[] oldTab = table; int oldCap = (oldTab == null) ? 0 : oldTab.length; int oldThr = threshold; int newCap, newThr = 0; if (oldCap > 0) { if (oldCap >= MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return oldTab; } else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) newThr = oldThr << 1; // double threshold } else if (oldThr > 0) // initial capacity was placed in threshold newCap = oldThr; else { // zero initial threshold signifies using defaults newCap = DEFAULT_INITIAL_CAPACITY; newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } if (newThr == 0) { float ft = (float)newCap * loadFactor; newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE); } threshold = newThr; @SuppressWarnings({"rawtypes","unchecked"}) Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; table = newTab; if (oldTab != null) { for (int j = 0; j < oldCap; ++j) { //遍歷舊鏈表 Node<K,V> e; if ((e = oldTab[j]) != null) { oldTab[j] = null; if (e.next == null) //單節點 newTab[e.hash & (newCap - 1)] = e; else if (e instanceof TreeNode) //樹 ((TreeNode<K,V>)e).split(this, newTab, j, oldCap); else { //鏈表 Node<K,V> loHead = null, loTail = null; Node<K,V> hiHead = null, hiTail = null; Node<K,V> next; do { next = e.next; if ((e.hash & oldCap) == 0) { if (loTail == null) loHead = e; else loTail.next = e; loTail = e; } else { //尾部指針hi if (hiTail == null) hiHead = e; else hiTail.next = e; hiTail = e; } } while ((e = next) != null); if (loTail != null) { loTail.next = null; newTab[j] = loHead; } if (hiTail != null) { hiTail.next = null; newTab[j + oldCap] = hiHead; } } } } } return newTab; }
這段代碼的含義是將oldTab[j]中的鏈表對半拆到newTab[j]和newTab[j + oldCap]
if ((e.hash & oldCap) == 0)怎麼理解
咱們知道oldTab中的index = (n - 1) & hash,假設n=8,則
newTab中的index = (16-1) & hash,那麼在newTab中的index爲index或者index + 8,那麼e.hash & oldCap == 0 ,hash 必然爲 X001000的形態纔有可能,也就是說
if ((e.hash & oldCap) == 0)表明newindex == index的狀況
loHead loTail/ hiTail hiHead 這2對指針
前面說了if ((e.hash & oldCap) == 0)表示newindex == index,那麼lo指針指向的就是此類節點,hi指針指向剩下的節點
經過tail指針的移動,實現鏈表拆分以及各節點next指針的更新
Dictionary, 線程安全
public synchronized V put(K key, V value) { // Make sure the value is not null if (value == null) { throw new NullPointerException(); } // Makes sure the key is not already in the hashtable. Entry<?,?> tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entry<K,V> entry = (Entry<K,V>)tab[index]; for(; entry != null ; entry = entry.next) { //遍歷鏈表 if ((entry.hash == hash) && entry.key.equals(key)) { V old = entry.value; entry.value = value; return old; } } addEntry(hash, key, value, index); return null; } private void addEntry(int hash, K key, V value, int index) { modCount++; Entry<?,?> tab[] = table; if (count >= threshold) { // Rehash the table if the threshold is exceeded rehash(); tab = table; hash = key.hashCode(); index = (hash & 0x7FFFFFFF) % tab.length; } // Creates the new entry. @SuppressWarnings("unchecked") Entry<K,V> e = (Entry<K,V>) tab[index]; tab[index] = new Entry<>(hash, key, value, e); count++; }
遍歷,從新計算index,並填入newMap
protected void rehash() { int oldCapacity = table.length; Entry<?,?>[] oldMap = table; // overflow-conscious code int newCapacity = (oldCapacity << 1) + 1; if (newCapacity - MAX_ARRAY_SIZE > 0) { if (oldCapacity == MAX_ARRAY_SIZE) // Keep running with MAX_ARRAY_SIZE buckets return; newCapacity = MAX_ARRAY_SIZE; } Entry<?,?>[] newMap = new Entry<?,?>[newCapacity]; modCount++; threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1); table = newMap; for (int i = oldCapacity ; i-- > 0 ;) { for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) { Entry<K,V> e = old; old = old.next; int index = (e.hash & 0x7FFFFFFF) % newCapacity; e.next = (Entry<K,V>)newMap[index]; //反轉鏈表 newMap[index] = e; } } }
紅黑樹是平衡二叉樹排序樹,咱們來看下它的特性
二叉排序樹特性
或者是具備下列性質的二叉樹
平衡二叉樹特性
紅黑樹的特性:
基於遞歸的思想處理
/*查找大於等於K的最小節點*/ final Entry<K,V> getCeilingEntry(K key) { Entry<K,V> p = root; while (p != null) { int cmp = compare(key, p.key); if (cmp < 0) { //key < p.key if (p.left != null) p = p.left; else return p; } else if (cmp > 0) { key > p.key if (p.right != null) { p = p.right; } else { //叔節點 Entry<K,V> parent = p.parent; Entry<K,V> ch = p; while (parent != null && ch == parent.right) { ch = parent; parent = parent.parent; } return parent; } } else return p; } return null; } /*查找小於等於K的最大節點, 原理相似,省略*/ final Entry<K,V> getFloorEntry(K key) { ... } /*查找大於K的最大節點, 原理相似,省略*/ final Entry<K,V> getHigherEntry(K key) { ... } /*查找小於K的最大節點, 原理相似,省略*/ final Entry<K,V> getLowerEntry(K key) { ... }
插入/刪除節點會破壞紅黑樹的平衡,爲了恢復平衡,能夠進行2類操做:旋轉和着色
private void rotateLeft(Entry<K,V> p) { if (p != null) { Entry<K,V> r = p.right; p.right = r.left; if (r.left != null) r.left.parent = p; r.parent = p.parent; if (p.parent == null) root = r; else if (p.parent.left == p) p.parent.left = r; else p.parent.right = r; r.left = p; p.parent = r; } }
插入節點老是爲紅色
情形1: 新節點位於樹的根上,沒有父節點
情形2: 新節點的父節點是黑色
情形3:新節點的父節點是紅色,分3類狀況
總結
當前節點的父節點是紅色,且當前節點的祖父節點的另外一個子節點(叔叔節點)也是紅色。
處理思路:
graph TD 1[祖父紅]-->2[父黑] 1-->3[叔黑] 2-->4{新紅} 2-->5[兄]
當前節點的父節點是紅色,叔叔節點是黑色,且當前節點是其父節點的右孩子
處理思路:
當前節點的父節點是紅色,叔叔節點是黑色,且當前節點是其父節點的左孩子
處理思路
private void fixAfterInsertion(Entry<K,V> x) { x.color = RED; //新插入的節點爲紅色 while (x != null && x != root && x.parent.color == RED) { // x的父節點 == x的父-父-左子節點 if (parentOf(x) == leftOf(parentOf(parentOf(x)))) { Entry<K,V> y = rightOf(parentOf(parentOf(x))); // y爲x的叔節點 if (colorOf(y) == RED) { //叔節點爲紅色, 3A setColor(parentOf(x), BLACK); setColor(y, BLACK); setColor(parentOf(parentOf(x)), RED); x = parentOf(parentOf(x)); } else { //叔節點爲黑色 if (x == rightOf(parentOf(x))) { //3B x = parentOf(x); rotateLeft(x); } //3C setColor(parentOf(x), BLACK); setColor(parentOf(parentOf(x)), RED); rotateRight(parentOf(parentOf(x))); } } else { Entry<K,V> y = leftOf(parentOf(parentOf(x))); if (colorOf(y) == RED) { //3A setColor(parentOf(x), BLACK); setColor(y, BLACK); setColor(parentOf(parentOf(x)), RED); x = parentOf(parentOf(x)); } else { if (x == leftOf(parentOf(x))) { //3C x = parentOf(x); rotateRight(x); } //3B setColor(parentOf(x), BLACK); setColor(parentOf(parentOf(x)), RED); rotateLeft(parentOf(parentOf(x))); } } } root.color = BLACK; }
狀況分析
private void deleteEntry(Entry<K,V> p) { modCount++; size--; // 狀況3 將p的值賦予後繼節點s,並轉換爲刪除s if (p.left != null && p.right != null) { Entry<K,V> s = successor(p); p.key = s.key; p.value = s.value; p = s; } // 狀況2 Entry<K,V> replacement = (p.left != null ? p.left : p.right); if (replacement != null) { // Link replacement to parent replacement.parent = p.parent; if (p.parent == null) root = replacement; else if (p == p.parent.left) p.parent.left = replacement; else p.parent.right = replacement; // Null out links so they are OK to use by fixAfterDeletion. p.left = p.right = p.parent = null; // Fix replacement if (p.color == BLACK) fixAfterDeletion(replacement); } else if (p.parent == null) { root = null; //p是根節點 } else { //狀況1 if (p.color == BLACK) fixAfterDeletion(p); if (p.parent != null) { if (p == p.parent.left) p.parent.left = null; else if (p == p.parent.right) p.parent.right = null; p.parent = null; } } }
刪除節點x,根據上文分析,x有0或1個子節點
若是x是黑色
處理方法:
處理方法:
處理方法
處理方法