美文网首页
JDK源码 -- HashMap

JDK源码 -- HashMap

作者: TomyZhang | 来源:发表于2019-07-30 20:39 被阅读0次

一、概念

类定义:

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable
  • 继承了AbstractMap抽象类,实现了Map接口,拥有一组Map通用的操作。
  • 实现了Cloneable接口,可进行浅层次拷贝。
  • 实现了Serializable接口,可进行序列化。

特点:

  • 允许键及值为空对象。
  • 非线程安全类,可通过Collections.synchronizedMap(new HashMap())获得线程安全的HashMap。
  • 不保证插入顺序,也不保证顺序不随时间变化。

二、使用

//TestHashMap
public class TestHashMap {
    private static final String TAG = "TestHashMap";
    private Map<String, Integer> map = new HashMap<>();

    public void testPut() {
        map.put("Android", 1);
        map.put(null, null);
        map.put("网络协议", 3);
        map.put("数据结构与算法", 4);
        Log.d(TAG, "zwm, put map: " + map);
        Log.d(TAG, "zwm, get key: null, value: " + map.get(null));
    }

    public void testPutAll() {
        Map<String, Integer> tempMap = new HashMap<>();
        tempMap.put("Android", 100);
        tempMap.put("Java", 200);
        tempMap.put("网络协议", 300);
        tempMap.put("数据结构与算法", 400);
        map.putAll(tempMap);
        Log.d(TAG, "zwm, putAll map: " + map);
    }

    public void testGet() {
        String key = "数据结构与算法";
        int value = map.get(key);
        Log.d(TAG, "zwm, get key: " + key + ", value: " + value);
    }

    public void testRemove() {
        String key = "网络协议";
        int value = map.remove(key);
        Log.d(TAG, "zwm, remove key: " + key + ", value: " + value);
    }

    public void testContains() {
        String key = "Android";
        boolean result = map.containsKey(key);
        Log.d(TAG, "zwm, containsKey: " + key + ", result: " + result);

        Integer value = 200;
        result = map.containsValue(value);
        Log.d(TAG, "zwm, containsValue: " + value + ", result: " + result);
    }

    public void testKeySet() {
        Set<String> keySet = map.keySet();
        Log.d(TAG, "zwm, keySet: " + keySet);
    }

    public void testValues() {
        Collection<Integer> values = map.values();
        Log.d(TAG, "zwm, values: " + values);
    }

    public void testEntrySet() {
        Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
        for(Map.Entry<String, Integer> entry : entrySet) {
            Log.d(TAG, "zwm, for entry, key: " + entry.getKey() + ", value: " + entry.getValue());
        }

        Iterator iterator = entrySet.iterator();
        while (iterator.hasNext()) {
            Map.Entry entry = (Map.Entry)iterator.next();
            Log.d(TAG, "zwm, iterator entry, key: " + entry.getKey() + ", value: " + entry.getValue());
        }
    }

    public void testClear() {
        map.clear();
        Log.d(TAG, "zwm, clear map: " + map);
    }

    public void testSize() {
        Log.d(TAG, "zwm, size: " + map.size());
    }

    public void testEmpty() {
        Log.d(TAG, "zwm, isEmpty: " + map.isEmpty());
    }
}

//测试代码
private void testMethod() {
    Log.d(TAG, "zwm, testMethod");
    TestHashMap testHashMap = new TestHashMap();
    testHashMap.testPut();
    testHashMap.testPutAll();
    testHashMap.testGet();
    testHashMap.testRemove();
    testHashMap.testContains();
    testHashMap.testKeySet();
    testHashMap.testValues();
    testHashMap.testEntrySet();
    testHashMap.testSize();
    testHashMap.testClear();
    testHashMap.testEmpty();
}

//输出log
2019-07-30 11:27:28.646 zwm, testMethod
2019-07-30 11:27:28.648 zwm, put map: {null=null, 数据结构与算法=4, 网络协议=3, Android=1}
2019-07-30 11:27:28.648 zwm, get key: null, value: null
2019-07-30 11:27:28.648 zwm, putAll map: {null=null, Java=200, 数据结构与算法=400, 网络协议=300, Android=100}
2019-07-30 11:27:28.649 zwm, get key: 数据结构与算法, value: 400
2019-07-30 11:27:28.649 zwm, remove key: 网络协议, value: 300
2019-07-30 11:27:28.649 zwm, containsKey: Android, result: true
2019-07-30 11:27:28.649 zwm, containsValue: 200, result: true
2019-07-30 11:27:28.649 zwm, keySet: [null, Java, 数据结构与算法, Android]
2019-07-30 11:27:28.650 zwm, values: [null, 200, 400, 100]
2019-07-30 11:27:28.650 zwm, for entry, key: null, value: null
2019-07-30 11:27:28.650 zwm, for entry, key: Java, value: 200
2019-07-30 11:27:28.650 zwm, for entry, key: 数据结构与算法, value: 400
2019-07-30 11:27:28.650 zwm, for entry, key: Android, value: 100
2019-07-30 11:27:28.650 zwm, iterator entry, key: null, value: null
2019-07-30 11:27:28.650 zwm, iterator entry, key: Java, value: 200
2019-07-30 11:27:28.651 zwm, iterator entry, key: 数据结构与算法, value: 400
2019-07-30 11:27:28.651 zwm, iterator entry, key: Android, value: 100
2019-07-30 11:27:28.651 zwm, size: 4
2019-07-30 11:27:28.651 zwm, clear map: {}
2019-07-30 11:27:28.651 zwm, isEmpty: true

三、原理

重要参数

//HashMap的初始容量为16,HashMap的容量指的是存储元素的数组大小,即桶的数量,必须为2的幂
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
//HashMap的最大容量为2的30次方,若传入的容量过大,将被最大值替换
static final int MAXIMUM_CAPACITY = 1 << 30;

//HashMap的负载因子,当size等于桶的数量*DEFAULT_LOAD_FACTOR时,就需要对HashMap进行扩容,扩容操作就是把桶的数量*2
//当DEFAULT_LOAD_FACTOR较小时,桶的利用率较低,但发生哈希冲突的概率也较小
//当DEFAULT_LOAD_FACTOR较大时,桶的利用率较高,但发生哈希冲突的概率也较大
static final float DEFAULT_LOAD_FACTOR = 0.75f;

//当某一个桶中链表的长度大于等于8时,链表结构会转换成红黑树结构
static final int TREEIFY_THRESHOLD = 8;
//当红黑树中的结点数量小于等于6时,红黑树结构会转变为链表结构
static final int UNTREEIFY_THRESHOLD = 6;
//当Node数组容量大于等于64的前提下,如果某一个桶中链表长度大于等于8,则会将链表结构转换成红黑树结构
static final int MIN_TREEIFY_CAPACITY = 64;

数组元素 & 链表结点

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() { //结点哈希值的计算方法:key的哈希值异或上value的哈希值
        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) { //重写equals方法
        if (o == this) //对象相等
            return true;
        if (o instanceof Map.Entry) { //对象为Map.Entry实例
            Map.Entry<?,?> e = (Map.Entry<?,?>)o;
            if (Objects.equals(key, e.getKey()) &&
                Objects.equals(value, e.getValue())) //key跟value的值都相等
                return true;
        }
        return false;
    }
}

红黑树结点

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);
    }   
    ...
}

构造函数

public HashMap() { //无参构造器
    this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}

public HashMap(int initialCapacity) { //指定容量大小,负载因子使用默认值
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
}

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);
}

public HashMap(Map<? extends K, ? extends V> m) { //将传入的Map参数中的全部元素逐个添加到HashMap中
    this.loadFactor = DEFAULT_LOAD_FACTOR;
    putMapEntries(m, false);
}

static final int tableSizeFor(int cap) { //返回一个大于等于且最接近cap的2的幂次方整数。例如给定9,返回2的4次方(即返回16)
    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;
}

分析tableSizeFor(int cap)方法:
假定cap形式为00..01XXXXXXX,(X代表可为0也可为1,X前面的1为从最高位开始第一个为1的那一位))
第一步: n |= n >>> 1; 也就是n变为n与n右移一位之后异或后的值,即 
n: 00..01XXXXXXX 
n>>>1: 00..001XXXXXX 
新n: 00..011XXXXXX 
第二步: n |= n >>> 2; 也就是n变成n与n右移两位之后异或的值,即 
n: 00..011XXXXXX 
n>>>2: 00..00011XXXX 
新n: 00..01111XXXX 
后面步骤类似。
这个算法不断地把第一个1后面的位全部变成1。
本例由00..01XXXXXXX —> 00..011111111,最后再返回n+1(2的幂次方)。 

public V put(K key, V value)

//插入方法,传递key值跟value值
public V put(K key, V value) { 
    return putVal(hash(key), key, value, false, true);
}

//将key值的哈希值异或该值右移16位后的值,得到新的哈希值
//key值可以为空,此时哈希值为0
static final int hash(Object key) { 
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

//插入数据
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    
    //若哈希表的数组为空,则通过resize()方法创建
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    
    //计算插入的位置索引:(n - 1) & hash
    if ((p = tab[i = (n - 1) & hash]) == null) //不存在哈希冲突
        //插入结点
        tab[i] = newNode(hash, key, value, null); 
    else { //存在哈希冲突
        Node<K,V> e; K k;
        //判断索引位置的结点的key值是否与要插入数据的key值相同
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k)))) //如果key值相同,则用新value值覆盖旧value值
            e = p;
        else if (p instanceof TreeNode) //如果key值不同,且索引位置的结点为红黑树结点,则在红黑树中插入或更新键值对
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else { //如果key值不同,且索引位置的结点为链表结点,则在链表中插入或更新键值对
            for (int binCount = 0; ; ++binCount) {
                //如果未找到相同的key值,则插入到表尾
                //如果链表结点数达到阈值,则将链表转换为红黑树
                if ((e = p.next) == null) { 
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                
                //如果找到相同的key值,则用新value值覆盖旧value值,并返回旧值
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    
    //插入成功后,如果实际存在的键值对数量size大于阈值,则调用resize()方法进行扩容
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

//向红黑树插入或更新数据
final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab, int h, K k, V v) {
    Class<?> kc = null;
    boolean searched = false;
    TreeNode<K,V> root = (parent != null) ? root() : this;
    for (TreeNode<K,V> p = root;;) {
        int dir, ph; K pk;
        if ((ph = p.hash) > h)
            dir = -1;
        else if (ph < h)
            dir = 1;
        else if ((pk = p.key) == k || (k != null && k.equals(pk)))
            return p;
        else if ((kc == null &&
                  (kc = comparableClassFor(k)) == null) ||
                 (dir = compareComparables(kc, k, pk)) == 0) {
            if (!searched) {
                TreeNode<K,V> q, ch;
                searched = true;
                if (((ch = p.left) != null &&
                     (q = ch.find(h, k, kc)) != null) ||
                    ((ch = p.right) != null &&
                     (q = ch.find(h, k, kc)) != null))
                    return q;
            }
            dir = tieBreakOrder(k, pk);
        }

        TreeNode<K,V> xp = p;
        if ((p = (dir <= 0) ? p.left : p.right) == null) {
            Node<K,V> xpn = xp.next;
            TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);
            if (dir <= 0)
                xp.left = x;
            else
                xp.right = x;
            xp.next = x;
            x.parent = x.prev = xp;
            if (xpn != null)
                ((TreeNode<K,V>)xpn).prev = x;
            moveRootToFront(tab, balanceInsertion(root, x));
            return null;
        }
    }
}

final void treeifyBin(Node<K,V>[] tab, int hash)

//将链表转成红黑树
final void treeifyBin(Node<K,V>[] tab, int hash) {
    int n, index; Node<K,V> e;
    if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) //链表转成红黑树的条件为数组最小容量大于等于MIN_TREEIFY_CAPACITY
        resize();
    else if ((e = tab[index = (n - 1) & hash]) != null) {
        TreeNode<K,V> hd = null, tl = null;
        do {
            TreeNode<K,V> p = replacementTreeNode(e, null);
            if (tl == null)
                hd = p;
            else {
                p.prev = tl;
                tl.next = p;
            }
            tl = p;
        } while ((e = e.next) != null);
        if ((tab[index] = hd) != null)
            hd.treeify(tab);
    }
}

final Node<K,V>[] resize()

//对哈希数组进行扩容
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; //扩大为原来的两倍
    }
    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 {
                            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;
}

//新索引:e.hash & (newCap - 1)
//扩容后,若哈希值新增参与运算的位为0,那么元素在扩容后位置为:原位置
//扩容后,若哈希值新增参与运算的位为1,那么元素在扩容后位置为:原位置 + 扩容前的旧容量

public V get(Object key)

//根据key值获取value值
public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}

final Node<K,V> getNode(int hash, Object key) {
    Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (first = tab[(n - 1) & hash]) != null) {
        if (first.hash == hash && //先检查第一个结点看是否满足要求
            ((k = first.key) == key || (key != null && key.equals(k))))
            return first;
        if ((e = first.next) != null) {
            if (first instanceof TreeNode) //如果是红黑树结点,则到红黑树中找
                return ((TreeNode<K,V>)first).getTreeNode(hash, key);
            do { //如果是链表结点,则遍历链表
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            } while ((e = e.next) != null);
        }
    }
    return null;
}

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

//将指定Map中的键值对复制到此Map中
public void putAll(Map<? extends K, ? extends V> m) {
    putMapEntries(m, true);
}

final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
    int s = m.size();
    if (s > 0) {
        if (table == null) { // pre-size
            float ft = ((float)s / loadFactor) + 1.0F;
            int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                     (int)ft : MAXIMUM_CAPACITY);
            if (t > threshold)
                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); //调用putVal方法进行插入操作
        }
    }
}

public V remove(Object key)

//删除键值对
public V remove(Object key) {
    Node<K,V> e;
    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;
    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;
        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);
            }
        }
        if (node != null && (!matchValue || (v = node.value) == value ||
                             (value != null && value.equals(v)))) {
            if (node instanceof TreeNode) //如果要删除的是红黑树结点
                ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
            else if (node == p) //如果要删除的是索引所在的结点
                tab[index] = node.next;
            else //如果要删除的是链表结点
                p.next = node.next;
            ++modCount;
            --size;
            afterNodeRemoval(node);
            return node;
        }
    }
    return null;
}

public boolean containsKey(Object key)

//判断是否存在相应的键
public boolean containsKey(Object key) {
    return getNode(hash(key), key) != null;
}

public boolean containsValue(Object value)

//判断是否存在相应的值
public boolean containsValue(Object value) {
    Node<K,V>[] tab; V v;
    if ((tab = table) != null && size > 0) {
        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;
}

public Set<K> keySet()

//获取key的Set集合
public Set<K> keySet() {
    Set<K> ks;
    return (ks = keySet) == null ? (keySet = new KeySet()) : ks;
}

public Collection<V> values()

//获取value的Collection集合
public Collection<V> values() {
    Collection<V> vs;
    return (vs = values) == null ? (values = new Values()) : vs;
}

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

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

public void clear()

//清空数组
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;
    }
}

public int size()

//获取HashMap元素个数
public int size() {
    return size;
}

public boolean isEmpty()

//判断HashMap是否为空
public boolean isEmpty() {
    return size == 0;
}

四、主题

哈希表
红黑树
LinkedHashMap

相关文章

网友评论

      本文标题:JDK源码 -- HashMap

      本文链接:https://www.haomeiwen.com/subject/rjuorctx.html