码迷,mamicode.com
首页 > 其他好文 > 详细

HashMap底层原理小记

时间:2018-08-24 16:04:14      阅读:192      评论:0      收藏:0      [点我收藏+]

标签:tab   equals   turn   内部类   原理   access   adf   直接   static   

  public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable

  首先HashMap 继承自AbstractMap(抽象类) 实现了Map接口。

  在new HashMap<K, V>()时,通过底层代码可以知道:

public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
        table = new Entry[DEFAULT_INITIAL_CAPACITY];
        init();
}

  其中loadFactor为float型,指hash表的负载因子;DEFAULT_INITIAL_CAPACITY 为常量,是hash表的初始容量,初值为16;threshold为int型,是hash表的装载阈值,反应了hash表的装载程度。接下来就会初始化一个Entry类型的数组,初始容量为16;而其中table的类型Entry是Hashmap的一个静态内部类。

static class Entry<K,V> implements Map.Entry<K,V>,并且该类没有无参构造函数,
  Entry(int h, K k, V v, Entry<K,V> n) {	
            value = v;
            next = n;
            key = k;
            hash = h;
        }

 当使用hashMap.put(K,V)时, 

public V put(K key, V value) {
        if (key == null)				//判断键是否为null
            return putForNullKey(value);	//添加键为null的值
        int hash = hash(key.hashCode());	//计算二次计算key的hash值,避免冲突
        int i = indexFor(hash, table.length);	//根据hash值确定元素存放在表中的索引
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {	//定位到table中的位置后,取出当前的链表
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {	//判断key是否重复
                V oldValue = e.value;					//重复的时候返回重复key的value值
                e.value = value;
                e.recordAccess(this);				//这句的作用是什么不知道
                return oldValue;
            }
        }
        modCount++;				// modCount是不可被序列化的,具有可见性的,
//用来记录hashmap的修改次数
        addEntry(hash, key, value, i);		//添加一个键值对
        return null;
}

  

void addEntry(int hash, K key, V value, int bucketIndex) {
	Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
        if (size++ >= threshold)
//当hash表的大小>=阈值时进行扩容,hash表的大小变为原来的两倍。
            resize(2 * table.length);
}

Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;
//直接让原来table[bucketIndex]位置上的表头为新元素的next值,说明,每次插入元素都会在表位置中的表头。
            next = n;
            key = k;
            hash = h;
        }

void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {//当hash表的容量到达最大值时不再扩容
            threshold = Integer.MAX_VALUE;
            return;
        }

        Entry[] newTable = new Entry[newCapacity];
        transfer(newTable);	//将原来hash表中的内容复制到新的hash表中
        table = newTable;
        threshold = (int)(newCapacity * loadFactor);
    }

  Hashmap中只允许有一个空键的原因:

  从代码中可以看出,当第二次添加空键的时候,会将原来null所对应的值覆盖,而且,空键所对应的key存放在表的第一个位置table[0].通过addEntry(0, null, value, 0),在第一次添加空键元素的时候,可以保证null所对应的元素在table[0]链表的表头。

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

 

HashMap底层原理小记

标签:tab   equals   turn   内部类   原理   access   adf   直接   static   

原文地址:https://www.cnblogs.com/wishboy/p/9529998.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!