【学习笔记-集合】HashMap 源码浅析

/**
 * HashMap主要方法解析,jdk1.7版本的HashMap
 * HashMap数据是通过数组和链表结合的方式(链表散列)存储。
 * 在put时候根据key值得到hash值(地址)即数组下标,之后如果得到相同下标则放在链表前面,之前的数据在链表尾部。                   
 * 前的数据在链表尾部。  
 *  在查找数据时候根据hashcode获取数组下标,在链表中使用equals根据key查找value.
 * 一、构造
 * 4个构造相对之前的jdk版本功能基本不变,但是代码封装更完善。
 * 构造前一个参数是容量,相当于数组大小,后一个是负载因子
 */
public HashMap(int initialCapacity, float loadFactor) {
        //当初始容量<0,抛出异常非法的参数容量
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        //初始容量不能大于最大容量值,最大容量值为MAXIMUM_CAPACITY = 1 << 30;   
        //左移一位相当于乘以2,所以左移30位相当于2^30.
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        //负载因子不为空并且<=0
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        //保存参数并且初始化数组
        this.loadFactor = loadFactor;
        threshold = initialCapacity;
        //此初始化将插入数据,主要使用Entry
        init();
    }
//无参构造默认容量是16,负载因子0.75
public HashMap() {
        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
    }
//指定容量参数,默认负载因子0.75
public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
/构造与指定map相同映射的新HashMap 
public HashMap(Map<? extends K, ? extends V> m) {
        this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
                      DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
        //如果容量不够,扩大table数组
        inflateTable(threshold);
        //将map中的元素添加到HashMap中
        putAllForCreate(m);
    }
 
/**
 * 二、创建数据链
 * 静态内部类,包含键值,节点next和hash值,由于他的存在,才会让table数组项以链表方式存在
 */
static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;
        int hash;
 
        //添加新条目
        Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }
 
        public final K getKey() {
            return key;
        }
 
        public final V getValue() {
            return value;
        }
 
        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }
        。
        。
        。
        。
        。
 /**
  *  三、存
  *  实现快速存取
  *  添加数据
  */
public V put(K key, V value) {
        //如果数组为空,添加数组容量
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        //如果key为空,保存null在table的第一个位置,所以HashMap可以为null
        if (key == null)
            return putForNullKey(value);
        //计算hash值
        int hash = hash(key);
        //计算key的hash值在table中的位置(索引)
        int i = indexFor(hash, table.length);
        //从i迭代e,找到key保存的位置
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            //判断该链上是否有hash(key)值相同的情况,若存在,则将其value值覆盖,保留新value
            //新值等于旧值,返回旧值
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        //修改次数加一
        modCount++;
        //将key,value值添加在i处
        addEntry(hash, key, value, i);
        return null;
    }
//都是添加Entry,这个是HashMap实际容量超过容器容量,下面的方法是没超过的情况
void addEntry(int hash, K key, V value, int bucketIndex) {
        //当HashMap大小不小于临界值(容量*负载因子)大小,并且数组bucketIndex位置不为null,改变HashMap大小,记录索引
        if ((size >= threshold) && (null != table[bucketIndex])) {
            resize(2 * table.length);
            hash = (null != key) ? hash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }
        //否则调用createEntry方法
        createEntry(hash, key, value, bucketIndex);
    }
 
//HashMap实际容量未超过默认容量或者初始化容量
void createEntry(int hash, K key, V value, int bucketIndex) {
        //保存bucketIndex的所在位置到e中
        Entry<K,V> e = table[bucketIndex];
        //设置bucketIndex位置元素为新Entry,并且设置e为新Entry下一个节点
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        size++;
    }
/**
  *  四、取
  *  实现快速存取
  *  获取数据
  */
public V get(Object key) {
        //若key为null,调用getForNullKey取出value
        if (key == null)
            return getForNullKey();
        //根据key值算出hash值并取出table对应索引处的值
        Entry<K,V> entry = getEntry(key);
 
        return null == entry ? null : entry.getValue();
    }
 
final Entry<K,V> getEntry(Object key) {
        if (size == 0) {
            return null;
        }
        //计算hash值
        int hash = (key == null) ? 0 : hash(key);
        //根据hash值取出table对应索引处的值
        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;
    }

猜你喜欢

转载自blog.csdn.net/weixin_36524613/article/details/80906258