HashMap源码探究
来源:互联网 发布:汶川地震知乎万人坑 编辑:程序博客网 时间:2024/06/09 23:22
HashMap的创建
public HashMap() { table = (HashMapEntry<K, V>[]) EMPTY_TABLE; threshold = -1; // Forces first put invocation to replace EMPTY_TABLE } public HashMap(int capacity) { if (capacity < 0) { throw new IllegalArgumentException("Capacity: " + capacity); } if (capacity == 0) { @SuppressWarnings("unchecked") HashMapEntry<K, V>[] tab = (HashMapEntry<K, V>[]) EMPTY_TABLE; table = tab; threshold = -1; // Forces first put() to replace EMPTY_TABLE return; } if (capacity < MINIMUM_CAPACITY) { capacity = MINIMUM_CAPACITY; } else if (capacity > MAXIMUM_CAPACITY) { capacity = MAXIMUM_CAPACITY; } else { capacity = Collections.roundUpToPowerOfTwo(capacity); } makeTable(capacity); } public HashMap(int capacity, float loadFactor) { this(capacity); if (loadFactor <= 0 || Float.isNaN(loadFactor)) { throw new IllegalArgumentException("Load factor: " + loadFactor); } /* * Note that this implementation ignores loadFactor; it always uses * a load factor of 3/4. This simplifies the code and generally * improves performance. */ }
构造函数中涉及到了capacity、loadFactor两个参数,这两个参数是影响HashMap性能的重要参数。
capacity代表了创建哈希表的初始容量,loadFactor代表哈希表容量自动增加之前可以达到多满,它衡量的是一个散列表的空间的使用程度,负载因子越大表示散列表的装填程度越高,反之愈小。对于使用链表法的散列表来说,查找一个元素的平均时间是O(1+a),因此如果负载因子越大,对空间的利用更充分,然而后果是查找效率的降低;如果负载因子太小,那么散列表的数据将过于稀疏,对空间造成严重浪费。系统默认负载因子为0.75
==事实上虽然第三个构造函数中可以指定loadFactor但是实际HashMap实现中并没有使用参数中的值而只是使用了默认的0.75==
如图:HashMap底层实现为数组table,数组的每一项又是一个链表HashMapEntry,而构造函数中capacity指的就是数组的长度
/** * Allocate a table of the given capacity and set the threshold accordingly. * @param newCapacity must be a power of two */ private HashMapEntry<K, V>[] makeTable(int newCapacity) { @SuppressWarnings("unchecked") HashMapEntry<K, V>[] newTable = (HashMapEntry<K, V>[]) new HashMapEntry[newCapacity]; table = newTable; threshold = (newCapacity >> 1) + (newCapacity >> 2); // 3/4 capacity return newTable; } static class HashMapEntry<K, V> implements Entry<K, V> { final K key; V value; final int hash; HashMapEntry<K, V> next; HashMapEntry(K key, V value, int hash, HashMapEntry<K, V> next) { this.key = key; this.value = value; this.hash = hash; this.next = next; } ... }
HashMap的数据存入
@Override public V put(K key, V value) { //key为空时 调用putValueForNullKey方法单独处理,这也是为什么HashMap支持null数据存取的原因 if (key == null) { return putValueForNullKey(value); } //计算key的hash值 int hash = Collections.secondaryHash(key); HashMapEntry<K, V>[] tab = table; //!!!计算hash对应的数组位置(h&(length - 1)就相当于对length取模,而且速度比直接取模快得多,这是HashMap在速度上的一个优化。) int index = hash & (tab.length - 1); //tab是否存在该hash值,如果存在则在数组对应的链表中查找是否有对应的key,如果有则新值覆盖旧值 for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) { //存在hash值和key值均相同的数据,执行新值覆盖旧值的动作 if (e.hash == hash && key.equals(e.key)) { preModify(e); V oldValue = e.value; e.value = value;//新值覆盖旧值 return oldValue;//返回旧值 } } //上面没有找到对应的key值 创建一个 // No entry for (non-null) key is present; create one modCount++; //判断是否需要扩容 if (size++ > threshold) { tab = doubleCapacity(); index = hash & (tab.length - 1); } //数组添加新的节点 addNewEntry(key, value, hash, index); return null; }
HashMap的读取
public V get(Object key) { //key为null时,调用entryForNullKey得到对应的value if (key == null) { HashMapEntry<K, V> e = entryForNullKey; return e == null ? null : e.value; } //计算key对应的hash值 int hash = Collections.secondaryHash(key); HashMapEntry<K, V>[] tab = table; //查找tab是否存在对应的key,如果存在返回对应的value for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)]; e != null; e = e.next) { K eKey = e.key; if (eKey == key || (e.hash == hash && key.equals(eKey))) { return e.value;//返回查找的value } } return null; }
HashMap的底层数组长度总是2的n次方,在构造函数中存在:capacity <<= 1;这样做总是能够保证HashMap的底层数组长度为2的n次方。当length为2的n次方时,h&(length - 1)就相当于对length取模,而且速度比直接取模快得多,这是HashMap在速度上的一个优化
HashMap中需要做到尽量的分布均匀且充分利用空间,这块的算法后面单说。
0 0
- HashMap源码探究
- 结合Java8源码探究HashMap原理
- Collection探究之HashMap
- HashMap详细探究
- HashMap和Hashtable探究
- 探究HashMap数据结构
- HashMap-Java8 HashMap实现原理探究
- HashMap 死循环的探究
- HashMap 死循环的探究
- HashMap 死循环的探究
- java HashMap 实现原理探究
- 探究HashMap的实现原理
- HashMap源码
- HashMap源码
- HashMap源码
- hashmap 源码
- HashMap源码
- HashMap源码
- [LInux]VScode的安装和调试
- 使用TP5数据库时的一些小积累
- javascript面试题
- SSIS Component的ValidateExternalMetadata属性
- 快速入门互联网协议
- HashMap源码探究
- 过渡动画的使用
- java复习思维导图
- Java常见设计模式
- 如何实现导航菜单栏中的二级下拉菜单?
- 设置centos7中的mysql5.7不区分表名大小写有关操作
- 单点登录原理与简单实现
- firefox 越来越不行了
- 【学习笔记02】java面向对象-成员变量、匿名对象