HashMap源码之get与put方法

版权声明:本文为博主原创文章,转载请注明作者与出处,http://blog.csdn.net/lixingtao0520 https://blog.csdn.net/lixingtao0520/article/details/83342301

HashMap是基于数组和链表来存储键值对对象的,我们简单看下get和put方法的源码。

1、我们调用put方法来存储键值对时,它先调用hash方法来计算hashcode,然后用hashcode和Entry数组的大小来做按位与操作,求出所在的Entry数组的下标位置。通过key与下标所在的Entry链表进行判断是否已经存在此Key,如果存在就重新设置value为新的值,不存在则加入到Entry链表中。加入链表是加入到原头结点之前,如下源码。如果key为null时,特殊处理,将null放到了第一个Entry链表中。

public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        if (key == null)
            return putForNullKey(value);
//计算hashcode
        int hash = hash(key);
//计算所在Entry数组(table)的下标
        int i = indexFor(hash, table.length);
//判断是否存在此Key,存在就替换value,并返回新值。
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
//modCount值增加,此值是iterator迭代时用来判断是否有其他线程对数据做过修改,如果做过修改则抛出ConcurrentModificationException
        modCount++;
//走到这,说明此key不存在于HashMap中,增加节点。
        addEntry(hash, key, value, i);
        return null;
    }

//增加节点方法,首先判断是否需要扩容,如果需要扩容则重新计算hash
void addEntry(int hash, K key, V value, int bucketIndex) {
        if ((size >= threshold) && (null != table[bucketIndex])) {
            resize(2 * table.length);
            hash = (null != key) ? hash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }
        createEntry(hash, key, value, bucketIndex);
    }
//创建节点方法,将Entry链表的头结点取出,将新Entry指向原来的头结点,table对应的下标指向新的头结点。
 void createEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        size++;
 }

//Entry的构造方法。
Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }
//如果是null值,直接放到第一个Entry链表中。
private V putForNullKey(V value) {
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
            if (e.key == null) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;
        addEntry(0, null, value, 0);
        return null;
    }

2、当调用get方法获取key时,对于null值特殊处理,直接从table[0]中获取。否则,计算key的hash值,找到key所在的Entry链表,遍历此链表找出对应的key值。

public V get(Object key) {
        if (key == null)
            return getForNullKey();
        Entry<K,V> entry = getEntry(key);
        return null == entry ? null : entry.getValue();
    }
//计算hash,遍历对应的链表。
final Entry<K,V> getEntry(Object key) {
        if (size == 0) {
            return null;
        }
        int hash = (key == null) ? 0 : hash(key);
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }
//null值从第一个Entry链表中获取。
private V getForNullKey() {
        if (size == 0) {
            return null;
        }
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
            if (e.key == null)
                return e.value;
        }
        return null;
    }

猜你喜欢

转载自blog.csdn.net/lixingtao0520/article/details/83342301
今日推荐