1. 程式人生 > 實用技巧 >【大資料】Hadoop的HDFS的API開發小實戰

【大資料】Hadoop的HDFS的API開發小實戰

HashMap是基於雜湊表的Map介面的實現。不能保證Node的順序。允許key和value為null。當然key是不能重複的。

繼承的類

  • AbstractMap 提供了一些Map基本實現的抽象類。

實現的介面

  • Map 聲明瞭一些常用的方法。
  • Cloneable 物件克隆標記介面。
  • Serializable 序列化介面。

靜態內部類Node

Node實現了Map.Entry介面。所以在本文中。Node和entry是一個東西。在jdk1.7中叫entry。

屬性

  • final int hash 當前Node的key的雜湊碼。是不可修改的。
  • final K key 當前Node的key。是不可修改的
  • V value 當前Node的value。
  • Node<K,V> next 當前Node指向的的下一個Node

方法

  • getKey 獲取當前Node的key。
  • getValue 返回當前Node的value。
  • setValue 修改當前Node的value。
  • equals 將當前Node與給定物件進行比較。
  • hashCode 獲取當前Node的key-value的雜湊碼Objects.hashCode(key) ^ Objects.hashCode(value)

內部類KeySet

繼承自抽象類AbstractSet,是要是提供了一些操作keySet的方法。

方法

  • size
    返回key的數量。
  • clear 清空map中的所有Node。
  • iterator 獲取keySet的迭代器。
  • contains 判斷keySet中是否包含給定key。
  • remove 將給定key物件的Node從map中刪除。
  • spliterator獲取keySet的可分割迭代器。
  • forEach為所有的key執行給定的action操作。

內部類Values

繼承了AbstractCollection抽象類,主要是提供一些操作values的方法

與上面的KeySet類似,但是Values沒有remove`方法。

內部類EntrySet

繼承自AbstractSet,主要是提供一些操作Node的方法。

  • size 返回Node的數量。
  • clear 清空map中所有的Node。
  • iterator 獲取entrySet的迭代器。
  • contains判斷entrySet是否包含給定的Node
  • remove 刪除給定的Node。
  • spliterator 返回Node的可分割迭代器。
  • forEach 為所有的Node執行給定action操作。

抽象內部類HashIterator

獲取遍歷map的迭代器。

屬性

  • next map中當前entry的下一個entry。
  • current 當前Node
  • expectedModCount 記錄當前的expectedModCount,用來進行快速失敗。
  • index 當前位於陣列的位置。

構造方法

這是在初始化上面的四個成員變數。

current預設是null,index預設是0,expectedModCount是呼叫當前構造其時map的modCount

HashIterator() {
    expectedModCount = modCount;
    Node<K,V>[] t = table;
    current = next = null;
    index = 0;
    if (t != null && size > 0) { // advance to first entry
        // 這裡是在遍歷table,找出陣列中index後面不為null的元素
        do {} while (index < t.length && (next = t[index++]) == null);
    }
}

方法

  • boolean hasNext() 判斷是否存在下一個Node。

  • Node<K,V> nextNode() 返回下一個Node(當前方法是不能被重寫的)

    final Node<K,V> nextNode() {
        Node<K,V>[] t;
        Node<K,V> e = next;
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        if (e == null)
            throw new NoSuchElementException();
        // 這裡會去當前Node所在的連結串列上去找。看他的下一個節點是否為null
        if ((next = (current = e).next) == null && (t = table) != null) {
            // 跟構造方法中的一樣。去遍歷table找到下一個不為null的元素。
            do {} while (index < t.length && (next = t[index++]) == null);
        }
        return e;
    }
    
  • void remove() 刪除current,這裡最終呼叫的map的removeNode刪除的。

KeyIteratorValueIteratorEntryIterator

三個類都繼承了HashIterator,並且實現了Iterator介面。

三個類都不能被繼承。

final class KeyIterator extends HashIterator
    implements Iterator<K> {
    public final K next() { return nextNode().key; }
}
final class ValueIterator extends HashIterator
    implements Iterator<V> {
    public final V next() { return nextNode().value; }
}
final class EntryIterator extends HashIterator
    implements Iterator<Map.Entry<K,V>> {
    public final Map.Entry<K,V> next() { return nextNode(); }
}

靜態內部類TreeNode

該類本質上是繼承了HashMap中的Node類。雖然表面上看到的是繼承了LinkedHashMap.Entry<K,V>,但是LinkedHashMap.Entry<K,V>是繼承了HashMap.Node<K,V>的。

TreeNodeNode的另一種形式。當map中table陣列的某一處連結串列長度>=8時,並且table.length >= 64時,會將連結串列轉為紅黑樹。此時Node就要升級成TreeNode。(如果table.length <64進對table進行擴容)

成員變數

  • static final int DEFAULT_INITIAL_CAPACITY = 1 << 4 預設初始容量為16,必須是2的冪。
  • static final int MAXIMUM_CAPACITY = 1 << 30; 最大容量2的30次冪。
  • static final float DEFAULT_LOAD_FACTOR = 0.75f 預設負載係數。
  • static final int TREEIFY_THRESHOLD = 8; 決定使用樹替換連結串列的閥值。當連結串列的深度大於8時並且當前table.lenght>=64時,將連結串列轉為紅黑樹。
  • static final int UNTREEIFY_THRESHOLD = 6 決定了使用連結串列替換樹的閥值。如果
  • static final int MIN_TREEIFY_CAPACITY = 64是擴容還是使用樹替換連結串列的閥值。64值的是table.length,一般都是與TREEIFY_THRESHOLD一起判斷
  • transient Node<K,V>[] table 存放Node的陣列。預設是null。每次擴容都是原來的2倍(oldCap<<1)
  • transient Set<Map.Entry<K,V>> entrySet 存放Node的Set集合。預設是null。
  • transient int size map中Node的數量。
  • transient int modCount 記錄了當前map的修改次數。主要用來做fail-fast
  • int threshold 要調整table大小的閥值(計算方式table.length * DEFAULT_LOAD_FACTOR)。當map的size大於了threshold,就對table進行擴容。
  • final float loadFactor 當前map的負載係數,負責控制map何時擴容。

構造方法

這裡所有的構造方法都不回去初始化table,也就是說建立的map物件預設容量是0。table的初始化是在第一次呼叫put方法時做的。

構造方法只會值設定map的負載係數loadFactor和要調整table大小的閥值threshold

建立一個map一般我們最多隻需要確定thresholdloadFactor

HashMap(int initialCapacity, float loadFactor)

給定了初始容量和負載係數的構造器。

給定的initialCapacity如果不是2的n次冪,就換將他轉為比他大的2的n次冪。比如initialCapacity=20,最後計算出的threshold=32

tableSizeFor(int cap)將給定的cap的二進位制的最高位後面的所有位都變成1,然後再加1,得到比cap大且最接近的2的n次冪的數。

public HashMap(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal initial capacity: " +
                                           initialCapacity);
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal load factor: " +
                                           loadFactor);
    this.loadFactor = loadFactor;
    this.threshold = tableSizeFor(initialCapacity);
}
static final int tableSizeFor(int cap) {
    // 0001 0100 20
    // 0001 0011 19
    int n = cap - 1;
    // 0000 1001 9
    // 0001 1011 27
    n |= n >>> 1;
    // 0000 0110 6
  	// 0001 1111 31
    n |= n >>> 2;
    // 0000 0001
    // 0001 1111 31
    n |= n >>> 4;
    n |= n >>> 8;
    n |= n >>> 16;
    return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}

HashMap(int initialCapacity)

指定map的初始容量。裡面是直接呼叫的上面的方法。

HashMap()

使用預設的負載係數建立map物件。

public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR;
}

HashMap(Map<? extends K, ? extends V> m)

使用給定map構造一個map物件。

使用預設的負載係數。使用給定map的size計算出threshold,知道這兩個值,就跟HashMap(int initialCapacity, float loadFactor)類似了。最後就是呼叫putVal了。

public HashMap(Map<? extends K, ? extends V> m) {
    this.loadFactor = DEFAULT_LOAD_FACTOR;
    putMapEntries(m, false);
}
final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
    int s = m.size();
    if (s > 0) {
        // 這個分支裡面的操作是為了計算出threshold,然後for迴圈呼叫putVal。
        if (table == null) { // pre-size
            // 計算出initialCapacity,+1.0F是位瞭解決float的小數,確保size足夠。
            float ft = ((float)s / loadFactor) + 1.0F;
            int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                     (int)ft : MAXIMUM_CAPACITY);
            if (t > threshold)
                // 計算出上面比t大且離t最近的2的n次冪的閥值。
                threshold = tableSizeFor(t);
        }
        else if (s > threshold)
            resize();
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
            K key = e.getKey();
            V value = e.getValue();
            putVal(hash(key), key, value, false, evict);
        }
    }
}

方法

int size()

獲取當前map中Node的數量。

boolean isEmpty()

判斷當前map中Node的數量是否為0。

V get(Object key)

根據給定的key找到對應Node,並返回Node中的value。

先用給定的key呼叫hash方法獲取其雜湊碼,然後呼叫getNode獲取key對應的Node

public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}
// 計算給定key的雜湊碼
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
// 根據給定的雜湊碼和key查詢Node
final Node<K,V> getNode(int hash, Object key) {
    Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
  	// 判斷key在table中的位置,並判斷該位置上的第一個Node是否不為null。
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (first = tab[(n - 1) & hash]) != null) {
        // 檢查第一個Node的雜湊碼和給定雜湊碼是否相同,並且key是否相同。如果都相同就直接返回該Node。
        if (first.hash == hash && // always check first node
            ((k = first.key) == key || (key != null && key.equals(k))))
            return first;
        // 如果第一個Node不是的,就去找該Node的下一個。
        if ((e = first.next) != null) {
            // 如果第一個Node是TreeNode,就說明當前儲存方式是紅黑樹,就用getTreeNode去查詢。
            if (first instanceof TreeNode)
                return ((TreeNode<K,V>)first).getTreeNode(hash, key);
            // 如果當前是連結串列儲存,就去遍歷找一下Node判斷他的雜湊碼和key。
            do {
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            } while ((e = e.next) != null);
        }
    }
    return null;
}

boolean containsKey(Object key)

判斷map中是否包含給定的key。

直接呼叫的getNode判斷是否不為null。

public boolean containsKey(Object key) {
    return getNode(hash(key), key) != null;
}

V put(K key, V value)

將給定的key和value儲存到map中。

put涉及到了一些操作

  • 給table擴容,分首次put初始化和給定容量初始化,還有超過閥值擴容
  • 將連結串列中的元素重寫計算位置之後,放到新的table中。因為擴容之後的容量是原來的2倍,所有(e.hash & oldCap)計算出來的結果如果等於0說明位置在oldCap範圍內,如果計算結果大於0說明位置咋愛oldCap外,(那麼外是什麼位置呢?),由於特殊的擴容方式,這裡的外就是在原來的位置上加上一個oldCap的距離。
  • 連結串列超過閥值,轉為紅黑樹
  • 如果key與存在的key衝突,直接使用當前value替換就的value,並返回舊value。

兩組測試資料:

0010 0000 假設oldCap=32
0001 0100 第一個hash=20 0000 0000 結果為0,在oldtab中的位置是(32-1)&20=20,在newTab中(64-1)&20=20
0010 0001 第二個hash=33 0010 0000 結果為32,在oldtab中的位置是(32-1)&33=1,在newTab中(64-1)&33=33,33=32+1

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}
// put鍵值對的方法
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
  	// 首先判斷table是否為空,是就去擴容。
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
  	// 判斷table中的i位置上是否有元素,(n - 1) & hash是計算當前key落在table哪個位置,
    if ((p = tab[i = (n - 1) & hash]) == null)
      	// 建立一個Node物件,並賦值給table的指定位置。
        tab[i] = newNode(hash, key, value, null);
    else {
      	// 如果說計算出的table所在位置i上面有元素
        Node<K,V> e; K k;
      	//如果當前要插入的key與table[i]位置上的Node的key相同,就把原來的Node p賦值給e,後面會進行判斷使用插入的value替換e的value
        if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
      	// 如果p是TreedNode,就呼叫TreeNode的putTreeVal方法
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
      	// 如果當前插入的鍵值對與table[i]位置上的Node的key不相同,並且當前元素也不是TreeNode,就去操作table[i]位置的連結串列
        else {
          	// for迴圈遍歷,記錄下連結串列的深度
            for (int binCount = 0; ; ++binCount) {
              	// 找到連結串列的尾部,將最後一個元素的next指向我們插入的新Node
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                  	// 如果連結串列的插入大於等於了8,就轉成紅黑樹
                    if (binCount >= TREEIFY_THRESHOLD - 1) // 
                        treeifyBin(tab, hash);
                    break;
                }
              	// 如果在遍歷的過程中,找到了與待插入key相同的key,就直接跳出迴圈,去指向外面的替換value的操作
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
      	// e表示我們找到的與待插入鍵值對key相同的Node
      	// 如果e不為null
        if (e != null) { // existing mapping for key
          	// oldValue存起來,一會兒返回的
            V oldValue = e.value;
          	// 如果onlyIfAbsent是false或者oldValue是null,就去替換value。
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
          	// 這個是空方法
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
  	// 如果是插入了一個新的Node,而不是替換的value,
  	// 就去把size+1,然後計算size是否超過了閥值threshold,如果超過了去擴容
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}
// 擴容方法
final Node<K,V>[] resize() {
    Node<K,V>[] oldTab = table;
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    int oldThr = threshold;
    int newCap, newThr = 0;
  	// 如果當前table的長度大於0
    if (oldCap > 0) {
      	// 如果大於等於了最大容量,就設定擴容閥值為int的最大值。
        if (oldCap >= MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return oldTab;
        }
      	// 如果當前table的容量乘以2小於最大容量,並且大於初始容量,就將容量擴容為原來的兩倍
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                 oldCap >= DEFAULT_INITIAL_CAPACITY)
            newThr = oldThr << 1; // double threshold
    }
  	// 這種一般是初始化的時候指定了容量,比如呼叫new HashMap(20)
    else if (oldThr > 0) // initial capacity was placed in threshold
        newCap = oldThr;
  	// 一般第一次呼叫put的時候,會使用預設容量對table進行初始化
    else {               // zero initial threshold signifies using defaults
        newCap = DEFAULT_INITIAL_CAPACITY;
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }
  	// 如果newThr是0,就使用newCap去計算newCap
    if (newThr == 0) {
        float ft = (float)newCap * loadFactor;
        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY 
                  (int)ft : Integer.MAX_VALUE);
    }
  	// 將新的擴容閥值賦值給threshold
    threshold = newThr;
    @SuppressWarnings({"rawtypes","unchecked"})
  	// 使用新的容量去建立Node陣列
    Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    table = newTab;
    if (oldTab != null) {
      	// 對舊的table中的元素重寫計算索引位置,放到新的newTab中。
        for (int j = 0; j < oldCap; ++j) {
            Node<K,V> e;
          	// 如果oldTab[j]位置上的元素不為null
            if ((e = oldTab[j]) != null) {
              	// 就將oldTab[j]設定為null
                oldTab[j] = null;
              	// 判斷他是否有next節點。沒有就直接將該元素放到newTab中。
                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 { // preserve order
                  	// 存放低位(0~oldCap)的Node
                    Node<K,V> loHead = null, loTail = null;
                  	// 存放高位(oldCap~oldCap+j)
                    Node<K,V> hiHead = null, hiTail = null;
                    Node<K,V> next;
                    do {
                        next = e.next;
                      	// 如果hash落在了0~oldCap內
// 0010 0000 假設oldCap=32
// 0001 0100 第一個hash=20 0000 0000 結果為0,在oldtab中的位置是(32-1)&20=20,在newTab中(64-1)&20=20
// 0010 0001 第二個hash=33 0010 0000 結果為32,在oldtab中的位置是(32-1)&33=1,在newTab中(64-1)&33=33
                        if ((e.hash & oldCap) == 0) {
                            if (loTail == null)
                                loHead = e;
                            else
                                loTail.next = e;
                            loTail = e;
                        }
                      	// 如果hash落在了oldCap~oldCap+j內
                        else {
                            if (hiTail == null)
                                hiHead = e;
                            else
                                hiTail.next = e;
                            hiTail = e;
                        }
                    } while ((e = next) != null);
                  	// 低位存放到newTab中
                    if (loTail != null) {
                        loTail.next = null;
                        newTab[j] = loHead;
                    }
                  	// 高位存放到newTab中
                    if (hiTail != null) {
                        hiTail.next = null;
                        newTab[j + oldCap] = hiHead;
                    }
                }
            }
        }
    }
    return newTab;
}

putAll(Map<? extends K, ? extends V> m)

將給定map中的元素新增到當前map中。

putMapEntries方法上面已經分析過了。

public void putAll(Map<? extends K, ? extends V> m) {
    putMapEntries(m, true);
}

V remove(Object key)

根據key刪除對應的Node,返回被刪除的Node。

public V remove(Object key) {
    Node<K,V> e;
  	// key表示要刪除的Node的key
  	// null表示Node的value
  	// false表示不比較value
  	// true表示可以移動其他Node
    return (e = removeNode(hash(key), key, null, false, true)) == null ?
        null : e.value;
}
final Node<K,V> removeNode(int hash, Object key, Object value,
                           boolean matchValue, boolean movable) {
    Node<K,V>[] tab; Node<K,V> p; int n, index;
  	// 判斷給定的key在map中是否存在
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (p = tab[index = (n - 1) & hash]) != null) {
        Node<K,V> node = null, e; K k; V v;
      	// 判斷index位置上的第一個元素是不是我們要刪除的元素,是的就將該元素賦值給node
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            node = p;
      	// 從連結串列(紅黑樹)中找到找到我們要刪除的元素
        else if ((e = p.next) != null) {
            if (p instanceof TreeNode)
                node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
            else {
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key ||
                         (key != null && key.equals(k)))) {
                        node = e;
                        break;
                    }
                    p = e;
                } while ((e = e.next) != null);
            }
        }
      	// 下面的條件分支與上面的是對應的
      	// 如果我們找到了要刪除的node,就matchValue=false,就不去比較value,
        if (node != null && (!matchValue || (v = node.value) == value ||
                             (value != null && value.equals(v)))) {
          	// 如果該節點是TreeNode就去呼叫removeTreeNode方法刪除
            if (node instanceof TreeNode)
                ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
          	// 如果index位置上的第一個節點就是我們要刪除的節點,就直接將該節點的下一個節點放在index位置上
            else if (node == p)
                tab[index] = node.next;
          	// 如果是在連結串列上找到的(非頭節點)
          	// p表示要刪除節點的上一個節點,next表示要刪除的節點
            else
              	// 將要刪除節點從連結串列中剔除
                p.next = node.next;
            ++modCount;
            --size;
            afterNodeRemoval(node);
          	// 返回被刪除的節點
            return node;
        }
    }
    return null;
}

void clear()

情況map中的所有Node。

public void clear() {
    Node<K,V>[] tab;
    modCount++;
    if ((tab = table) != null && size > 0) {
        size = 0;
        for (int i = 0; i < tab.length; ++i)
            tab[i] = null;
    }
}

boolean containsValue(Object value)

判斷map中是否包含給定的key

public boolean containsValue(Object value) {
    Node<K,V>[] tab; V v;
    if ((tab = table) != null && size > 0) {
      	// 遍歷table
        for (int i = 0; i < tab.length; ++i) {
          	// 遍歷連結串列
            for (Node<K,V> e = tab[i]; e != null; e = e.next) {
                if ((v = e.value) == value ||
                    (value != null && value.equals(v)))
                    return true;
            }
        }
    }
    return false;
}

Set<K> keySet()

返回key的集合

public Set<K> keySet() {
    Set<K> ks = keySet;
    if (ks == null) {
        ks = new KeySet();
        keySet = ks;
    }
    return ks;
}

Collection<V> values()

返回map中value的集合

public Collection<V> values() {
    Collection<V> vs = values;
    if (vs == null) {
        vs = new Values();
        values = vs;
    }
    return vs;
}

Set<Map.Entry<K,V>> entrySet()

返回map中的Node集合

public Set<Map.Entry<K,V>> entrySet() {
    Set<Map.Entry<K,V>> es;
    return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}

V getOrDefault(Object key, V defaultValue)

根據key獲取對應的value,如果key不存在,就返回defaultValue

public V getOrDefault(Object key, V defaultValue) {
    Node<K,V> e;
    return (e = getNode(hash(key), key)) == null ? defaultValue : e.value;
}

V putIfAbsent(K key, V value)

如果給定的key不存在就是將key-value儲存到map中,如果給定的key存在,並且value是null,將使用給定的value替換,然後返回null。如果舊的value不為null,就不做任何修改直接返回舊的value。

public V putIfAbsent(K key, V value) {
  	// 第一個true表示不修改已經存在的值
		// 第二個true表示table不是建立模式
    return putVal(hash(key), key, value, true, true);
}

boolean remove(Object key, Object value)

刪除給定的鍵值對。

public boolean remove(Object key, Object value) {
  	// 第一個ture表示是否vaule匹配才刪除
    return removeNode(hash(key), key, value, true, true) != null;
}

boolean replace(K key, V oldValue, V newValue)

將指定key-value的value使用給定的新value替換,替換成功返回true

public boolean replace(K key, V oldValue, V newValue) {
    Node<K,V> e; V v;
    if ((e = getNode(hash(key), key)) != null &&
        ((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) {
        e.value = newValue;
        afterNodeAccess(e);
        return true;
    }
    return false;
}

V replace(K key, V value)

使用給定的value替換給定key的value。返回舊的value的值。

public V replace(K key, V value) {
    Node<K,V> e;
    if ((e = getNode(hash(key), key)) != null) {
        V oldValue = e.value;
        e.value = value;
        afterNodeAccess(e);
        return oldValue;
    }
    return null;
}

V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)

如果給定key的value為null,就使用mappingFunction計算出一個value,如果計算出的value不為null就將該value設定為指定key的value。

public V computeIfAbsent(K key,
                         Function<? super K, ? extends V> mappingFunction) {
    if (mappingFunction == null)
        throw new NullPointerException();
    int hash = hash(key);
    Node<K,V>[] tab; Node<K,V> first; int n, i;
    int binCount = 0;
    TreeNode<K,V> t = null;
    Node<K,V> old = null;
  	// 看要不要去擴容
    if (size > threshold || (tab = table) == null ||
        (n = tab.length) == 0)
        n = (tab = resize()).length;
  	// 找到給定key對應的Node
    if ((first = tab[i = (n - 1) & hash]) != null) {
        if (first instanceof TreeNode)
            old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
        else {
            Node<K,V> e = first; K k;
            do {
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k)))) {
                    old = e;
                    break;
                }
                ++binCount;
            } while ((e = e.next) != null);
        }
        V oldValue;
        if (old != null && (oldValue = old.value) != null) {
            afterNodeAccess(old);
            return oldValue;
        }
    }
  	// 計算出新的value
    V v = mappingFunction.apply(key);
    if (v == null) {
        return null;
    } else if (old != null) {
      	// 如果給定的key存在,並且計算出的value不為null,就是使用計算出的value替換就的value
        old.value = v;
        afterNodeAccess(old);
        return v;
    }
  	// 如果是紅黑樹
    else if (t != null)
      	// 呼叫紅黑樹的putTreeVal替換value
        t.putTreeVal(this, tab, hash, key, v);
    else {
      	// 如果map中不存給定key的鍵值對,就建立Node,存入
        tab[i] = newNode(hash, key, v, first);
      	// 計算當前連結串列是否需要轉為紅黑樹
        if (binCount >= TREEIFY_THRESHOLD - 1)
            treeifyBin(tab, hash);
    }
    ++modCount;
    ++size;
    afterNodeInsertion(true);
    return v;
}

V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)

如果給定key的value不為null,就使用remappingFunction計算出一個value,如果該value不為null,就設定給指定的key並返回。如果給定key的value為null,就刪除該key-value。

public V computeIfPresent(K key,
                          BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
    if (remappingFunction == null)
        throw new NullPointerException();
    Node<K,V> e; V oldValue;
    int hash = hash(key);
    if ((e = getNode(hash, key)) != null &&
        (oldValue = e.value) != null) {
        V v = remappingFunction.apply(key, oldValue);
        if (v != null) {
            e.value = v;
            afterNodeAccess(e);
            return v;
        }
        else
            removeNode(hash, key, null, false, true);
    }
    return null;
}

V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)

如果remappingFunction計算處的新value不為null,就將key-value儲存到map中。如果新value為null,並且給定key是存在的,就刪除給定key對應鍵值對。

public V compute(K key,
                 BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
    if (remappingFunction == null)
        throw new NullPointerException();
    int hash = hash(key);
    Node<K,V>[] tab; Node<K,V> first; int n, i;
    int binCount = 0;
    TreeNode<K,V> t = null;
    Node<K,V> old = null;
    if (size > threshold || (tab = table) == null ||
        (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((first = tab[i = (n - 1) & hash]) != null) {
        if (first instanceof TreeNode)
            old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
        else {
            Node<K,V> e = first; K k;
            do {
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k)))) {
                    old = e;
                    break;
                }
                ++binCount;
            } while ((e = e.next) != null);
        }
    }
    V oldValue = (old == null) ? null : old.value;
    V v = remappingFunction.apply(key, oldValue);
    if (old != null) {
        if (v != null) {
            old.value = v;
            afterNodeAccess(old);
        }
        else
            removeNode(hash, key, null, false, true);
    }
    else if (v != null) {
        if (t != null)
            t.putTreeVal(this, tab, hash, key, v);
        else {
            tab[i] = newNode(hash, key, v, first);
            if (binCount >= TREEIFY_THRESHOLD - 1)
                treeifyBin(tab, hash);
        }
        ++modCount;
        ++size;
        afterNodeInsertion(true);
    }
    return v;
}

V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction)

如果給定的key存在,並且value不為null,就用remappingFunction計算出來的結果替換value(如果計算出的value為null就刪除該節點),如果value為null,就直接使用給定的value替換舊的value。如果給定key不存在,就將給定的key-value儲存到map中。

public V merge(K key, V value,
               BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
    if (value == null)
        throw new NullPointerException();
    if (remappingFunction == null)
        throw new NullPointerException();
    int hash = hash(key);
    Node<K,V>[] tab; Node<K,V> first; int n, i;
    int binCount = 0;
    TreeNode<K,V> t = null;
    Node<K,V> old = null;
  	// 擴容
    if (size > threshold || (tab = table) == null ||
        (n = tab.length) == 0)
        n = (tab = resize()).length;
  	// 找Node
    if ((first = tab[i = (n - 1) & hash]) != null) {
        if (first instanceof TreeNode)
            old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
        else {
            Node<K,V> e = first; K k;
            do {
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k)))) {
                    old = e;
                    break;
                }
                ++binCount;
            } while ((e = e.next) != null);
        }
    }
  	// 找到的Node不為null
    if (old != null) {
        V v;
      	// 如果Node的value不為null,使用給定的value和key對應的value計算出一個新的value
        if (old.value != null)
            v = remappingFunction.apply(old.value, value);
        else
          	// 如果Node的value是null,直接將給定value作為新value
            v = value;
      	// 新value不為null,就直接替換Node的value
        if (v != null) {
            old.value = v;
            afterNodeAccess(old);
        }
        else
          	// 如果計算出的新的value為null,就刪除該Node
            removeNode(hash, key, null, false, true);
        return v;
    }
  	// 如果找不到給定key的Node
    if (value != null) {
      	// 如果是紅黑樹,就呼叫putTreeVal建立新的節點
        if (t != null)
            t.putTreeVal(this, tab, hash, key, value);
        else {
          	// 如果是連結串列,就直接newNode
            tab[i] = newNode(hash, key, value, first);
          	// 判斷連結串列是否需要轉為紅黑樹
            if (binCount >= TREEIFY_THRESHOLD - 1)
                treeifyBin(tab, hash);
        }
        ++modCount;
        ++size;
        afterNodeInsertion(true);
    }
    return value;

forEach(BiConsumer<? super K, ? super V> action)

遍歷map中的所有key-value對。

public void forEach(BiConsumer<? super K, ? super V> action) {
    Node<K,V>[] tab;
    if (action == null)
        throw new NullPointerException();
    if (size > 0 && (tab = table) != null) {
        int mc = modCount;
        for (int i = 0; i < tab.length; ++i) {
            for (Node<K,V> e = tab[i]; e != null; e = e.next)
                action.accept(e.key, e.value);
        }
        if (modCount != mc)
            throw new ConcurrentModificationException();
    }
}

void replaceAll(BiFunction<? super K, ? super V, ? extends V> function)

遍歷所有Node,使用function計算出新的value,替換舊的value

public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
    Node<K,V>[] tab;
    if (function == null)
        throw new NullPointerException();
    if (size > 0 && (tab = table) != null) {
        int mc = modCount;
        for (int i = 0; i < tab.length; ++i) {
            for (Node<K,V> e = tab[i]; e != null; e = e.next) {
                e.value = function.apply(e.key, e.value);
            }
        }
        if (modCount != mc)
            throw new ConcurrentModificationException();
    }
}

總結

HashMap預設使用無參構造器去建立事,底層table陣列預設是null,只有在第一次put資料的時候才會去初始化table,預設初始化容量為16,擴容閥值是12,最大容量是2的30次方。預設負載係數的0.75,擴容閥值=負載係數*容量

如果連結串列的長度大於8,並且table.length>=64,就講連結串列轉為紅黑樹,如果紅黑樹的節點<=6就講紅黑樹退化成連結串列。

作為key的物件,必須重寫equalshashCode方法。