HashMap源码分析
来源:互联网 发布:外国人审美 知乎 编辑:程序博客网 时间:2024/05/17 03:56
hashmap内中维护的是Entry类型的数组以及单向链表。Entry类型是静态内部类,内含key,value,next,hash四个属性。
hashmap中还有两个重要的参数:threshold和loadFactor,分别为扩容阈值和负载因子,它们之间的关系为threshold=capacity*loadFactor。
构造器
hashmap有多个重载的构造器,最核心的构造器为
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; threshold = initialCapacity; init(); }
其余构造器都是调用此构造器。另外有一个由已有map对象构造的构造器如下:
public HashMap(Map<? extends K, ? extends V> m) { this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR); inflateTable(threshold); putAllForCreate(m); } private static int roundUpToPowerOf2(int number) { // assert number >= 0 : "number must be non-negative"; return number >= MAXIMUM_CAPACITY ? MAXIMUM_CAPACITY : (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1; } private void inflateTable(int toSize) { // Find a power of 2 >= toSize int capacity = roundUpToPowerOf2(toSize); threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1); table = new Entry[capacity]; initHashSeedAsNeeded(capacity); }
此构造器先调用之前构造器进行构造,此时阈值只是最小默认容量和入参对象的size/默认负载因子的最大值,然后调用inflateTable方法计算大于等于该阈值的最小的2的幂函数,令该幂函数的值为容量大小。最后给给数组赋值。
基础设施
indexFor()/hash()方法
static int indexFor(int h, int length) { // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2"; return h & (length-1); //等价于h%length }
indexFor()方法用于返回hash值对应的数组索引。属于访问table数组的前哨方法,访问table数组都需要先让该方法通过hash和数组长度计算索引再执行访问。hash值就像是对象的ID用来标识对象。
因为前面数组的长度始终是2的幂函数,则length-1生成低位掩码(如:length=16,length-1=15,二进制形式即为00000000 00000000 00000000 00001111),与h按位与返回的是h模length。但是对于int这么大的值空间,每次都取低位相与,比较容易碰撞,所以hashmap还有一个函数hash()对key的hash值进行预处理:
final int hash(Object k) { int h = hashSeed; if (0 != h && k instanceof String) { return sun.misc.Hashing.stringHash32((String) k); } h ^= k.hashCode(); // This function ensures that hashCodes that differ only by // constant multiples at each bit position have a bounded // number of collisions (approximately 8 at default load factor). h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); }
这段代码叫扰动函数。顾名思义,就是“扰动”参数对象的hash值,重点是要使其低位部分更具有随机性,以减少调用indexFor时碰撞的可能性。这里各种各种右移和异或操作,即为扰动过程,高位和低位异或也变相保留了原hash值的高位特征。
indexFor和hash都是访问数组的前哨方法,先hash扰动,再计算索引。
hashmap中key为null的Entry对象始终存储在table[0]位置上,即null始终在数组的第一个位置上,null对应的hash值为0。当然其他不是null的key,也可能将Entry对象存储在table[0]的链表中。
Entry< K,V>对象
静态内部类Entry类,实现了Map接口的内部接口Entry,是Hashmap的逻辑上的数据单元。其中next属性,用于形成单向链表。
static class Entry<K,V> implements Map.Entry<K,V> { final K key; V value; Entry<K,V> next; int hash; /** * Creates new entry. */ 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 final boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry e = (Map.Entry)o; Object k1 = getKey(); Object k2 = e.getKey(); if (k1 == k2 || (k1 != null && k1.equals(k2))) { Object v1 = getValue(); Object v2 = e.getValue(); if (v1 == v2 || (v1 != null && v1.equals(v2))) return true; } return false; } public final int hashCode() { return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue()); } public final String toString() { return getKey() + "=" + getValue(); } /** * This method is invoked whenever the value in an entry is * overwritten by an invocation of put(k,v) for a key k that's already * in the HashMap. */ void recordAccess(HashMap<K,V> m) { } /** * This method is invoked whenever the entry is * removed from the table. */ void recordRemoval(HashMap<K,V> m) { } }
增
createEntry(int hash,K key,V value,int bucketIndex)
包访问权限。
创建一个Entry,现将原桶位上的Entry的引用保存到局部变量,然后在该桶位上创建新Entry对象,最后将原Entry对象的引用赋值给新对象的next属性。
这样会保证桶位链表最新存入的数据会在第一个位置上,由于概率上新写入的数据很可能在将来不久会继续访问,放在第一个位置上减少遍历链表的开销。
transfer(Entry[] newTable,boolean rehash)
将旧table中的Entry对象都转移至新数组。遍历旧table对象,对于每一个Entry对象,调用indexFor方法在新数组中寻找桶位,将该桶位原有值引用赋值给next属性,将新Entry对象引用赋值给桶位。
resize(int newCapacity)
新建数组,调用transfer方法,然后将新数组引用赋值给当前map。
addEntry(int hash,K key,V value,int bucketIndex)
不同于createEntry(),addEntry需要考虑resize问题,createEntry()单纯只负责创建。
先检查size是否超过阈值,同时bucketIndex不为null,则将原数组扩大两倍,然后在新数组上createEntry。
putForNullKey(V value)
1、直接取table[0]上的Entry对象,遍历该链表,如果存在key为null的Entry对象,则用新值覆盖旧值;如果不存在,则调用addEntry方法添加。2、如果该链表没有key为null的Entry,则调用addEntry在table[0]的桶位新增Entry对象。
put(K key,V value)
在map中新增Entry。1、如果key为null,调用putForNullKey()。
2、执行前哨方法(先扰动hash,然后由新hash和tablelength经indexFor获取index),找到桶位,如果存在链表,则遍历查找看是否有同样的key的Entry,修改其value为新值;如果不存在链表或者没有同样的key,则调用addEntry()。
putForCreate(K key,V value)
对每个Entry,先执行前哨方法,然后再找table中已有的进行覆盖,然后调用createEntry()。
putAllForCreate(Map< ? extends K,? extends V> m)
构造器中从一个map对象构造hashmap,对map中的每个Entry,调用putForCreate()。
putAll(Map< ? extends K,? extends V> m)
先保证容量,然后对每个Entry,调用put()方法
put方法分两类,一类是在已有map上增加(add),一类是构造(create)。两者的区别在于,add时需要考虑容量的问题,修改modCount值。而create不需要考虑这些问题。
删
removeEntryForKey(Object key)
先执行前哨方法找到桶位,然后遍历链表,修改modCount和size,修改桶位引用和链表引用。
remove(Object key)
调用removeEntryForKey(),与它不同的是它返回Entry,而remove只返回value。
removeMapping(Object o)
删除一个Entry,并返回该Entry。如果o不是Map.Entry的实例,返回null。
clear()
调用Arrays.fill()方法,填充数组各个桶位的引用为null。modCount++,size =0。
改
直接调用put()方法。
查
getForNullKey()
遍历table[0]。
getEntry(Object key)
执行前哨方法,找到桶位,遍历链表。
get()
null判断,再getEntry()
containsKey()
判断getEntry的结果
containsValue(Object value)
两级for循环遍历数组和链表
迭代器
通过一个map进行迭代要比collection复杂,因为map不提供迭代器,而是提供三种方法,将map对象的视图作为collection对象返回。由于这些视图本身就是collection,因此它们可以被迭代。
内部抽象类HashIterator实现了Iterator接口
private abstract class HashIterator<E> implements Iterator<E> { Entry<K,V> next; // next entry to return int expectedModCount; // For fast-fail int index; // current slot Entry<K,V> current; // current entry HashIterator() { expectedModCount = modCount; if (size > 0) { // advance to first entry Entry[] t = table; //迭代语句 while (index < t.length && (next = t[index++]) == null) ; } } public final boolean hasNext() { return next != null; } final Entry<K,V> nextEntry() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); Entry<K,V> e = next; if (e == null) throw new NoSuchElementException(); //如果链表遍历完成 if ((next = e.next) == null) { Entry[] t = table; //继续寻找下一个引用不为null的桶位,这里利用while循环不断的判断条件,并在判断的过程之中给next赋值。直到next不为null,继续遍历链表 while (index < t.length && (next = t[index++]) == null) ; } current = e; return e; } public void remove() { if (current == null) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); Object k = current.key; current = null; HashMap.this.removeEntryForKey(k); expectedModCount = modCount; } }
构造器中将table[0]中的引用赋值给next,此时next拿到数组第一个Entry对象,然后访问该对象的next属性,遍历链表,如果链表遍历完成,则寻找下一个不为null的桶位,继续遍历链表…..直到数组中所有Entry都遍历完成。
HashIterator有三个子类,分别实现了各自的next()方法。
private final class ValueIterator extends HashIterator<V> { public V next() { return nextEntry().value; } } private final class KeyIterator extends HashIterator<K> { public K next() { return nextEntry().getKey(); } } private final class EntryIterator extends HashIterator<Map.Entry<K,V>> { public Map.Entry<K,V> next() { return nextEntry(); } }
视图
HashMap有三个视图:keySet(),values(),entrySet(),这三个视图均由HashIterator返回的Entry对象直接支持,而HashIterator由HashMap本身直接支持,所以对视图的操作将直接影响到HashMap本身,相反也成立。
keySet()和entrySet()均继承了AbstractSet类,values()则继承了AbstractCollection类。这些类的迭代器实现由上面三个迭代器分别提供支持。其他方法,由HashMap本身属性和方法提供支持。
//keySet视图public Set<K> keySet() { Set<K> ks = keySet; return (ks != null ? ks : (keySet = new KeySet())); } private final class KeySet extends AbstractSet<K> { public Iterator<K> iterator() { return newKeyIterator(); } public int size() { return size; } public boolean contains(Object o) { return containsKey(o); } public boolean remove(Object o) { return HashMap.this.removeEntryForKey(o) != null; } public void clear() { HashMap.this.clear(); } }//values视图public Collection<V> values() { Collection<V> vs = values; return (vs != null ? vs : (values = new Values())); } private final class Values extends AbstractCollection<V> { public Iterator<V> iterator() { return newValueIterator(); } public int size() { return size; } public boolean contains(Object o) { return containsValue(o); } public void clear() { HashMap.this.clear(); } }//entry视图public Set<Map.Entry<K,V>> entrySet() { return entrySet0(); } private Set<Map.Entry<K,V>> entrySet0() { Set<Map.Entry<K,V>> es = entrySet; return es != null ? es : (entrySet = new EntrySet()); } private final class EntrySet extends AbstractSet<Map.Entry<K,V>> { public Iterator<Map.Entry<K,V>> iterator() { return newEntryIterator(); } public boolean contains(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<K,V> e = (Map.Entry<K,V>) o; Entry<K,V> candidate = getEntry(e.getKey()); return candidate != null && candidate.equals(e); } public boolean remove(Object o) { return removeMapping(o) != null; } public int size() { return size; } public void clear() { HashMap.this.clear(); } }
参考内容
https://www.zhihu.com/question/20733617/answer/111577937
- 源码分析:HashMap
- 源码分析:HashMap
- HashMap源码分析
- HashMap 源码分析
- HashMap源码分析
- HashMap LinkedHashMap源码分析
- HashMap源码分析
- HashMap 源码分析
- HashMap源码分析
- HashMap源码分析
- HashMap源码分析
- Java HashMap 源码分析
- HashMap源码分析
- java HashMap源码分析
- 源码分析HashMap
- HashMap源码分析
- HashMap源码分析
- HashMap源码分析
- 七个方法让你的unity项目更条理
- 购物车模块的功能设计
- siteServer CMS 建站流程
- tomcat支持文件下载
- yii2的components详解
- HashMap源码分析
- C++ new Class
- AppiumLibrary导入红色的解决方案
- 图像处理之信息隐藏
- Android APK 瘦身
- linux后端诊断与调试技术
- 设置div背景颜色透明度,内部元素不透明
- #比特币病毒#来袭,教你关闭445端口(附打开方法)
- 数据库连接池--c3p0