HashMap源码详解
来源:互联网 发布:centos 6.5 漏洞 编辑:程序博客网 时间:2024/06/08 12:15
HashMap中有个重要的数据HashMapEntry,在源码里面有介绍
static class HashMapEntry<K, V> implements Entry<K, V> { final K key; V value; final int hash; HashMapEntry<K, V> next; ...... }
Entry 是一个 static class,其中包含了 key 和 value,也就是键值对,另外还包含了一个 next 的 Entry 指针。
我们可以总结出:Entry 就是数组中的元素,每个 Entry 其实就是一个 key-value 对,它持有一个指向下一个元素的引用,这就构成了链表。
1.源码详解:
public class HashMap<K, V> extends AbstractMap<K, V> implements Cloneable, Serializable { // 存数据的数组 transient HashMapEntry<K, V>[] 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); } private HashMapEntry<K, V>[] makeTable(int newCapacity) { @SuppressWarnings("unchecked") // 几乎所有的构造函数都会有new HashMapEntry[newCapacity]; HashMapEntry<K, V>[] newTable = (HashMapEntry<K, V>[]) new HashMapEntry[newCapacity]; table = newTable; threshold = (newCapacity >> 1) + (newCapacity >> 2); // 3/4 capacity return newTable; } // **************************以上就是构造函数,很明显就是new一个HashMapEntry数组出来以便后续保存数据 // 获取数据。先计算key的hashCode。然后逐个访问链表,如果K相同或者hash值和key值完全相等,就返回值(不高效) public V get(Object key) { if (key == null) { HashMapEntry<K, V> e = entryForNullKey; return e == null ? null : e.value; } // 计算key的hashCode int hash = key.hashCode(); hash ^= (hash >>> 20) ^ (hash >>> 12); hash ^= (hash >>> 7) ^ (hash >>> 4); HashMapEntry<K, V>[] tab = table; for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)]; e != null; e = e.next) { K eKey = e.key; // 如果K相同或者hash值和key值完全相等,就返回值 if (eKey == key || (e.hash == hash && key.equals(eKey))) { return e.value; } } return null; } // 赋值操作。问题和get一样,逐个访问不高效 public V put(K key, V value) { if (key == null) { return putValueForNullKey(value); } int hash = secondaryHash(key); HashMapEntry<K, V>[] tab = table; int index = hash & (tab.length - 1); for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) { if (e.hash == hash && key.equals(e.key)) { preModify(e); V oldValue = e.value; e.value = value; return oldValue; } } // 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; } // 移除某个Entry,相当于链表里面移除某个节点 public V remove(Object key) { if (key == null) { return removeNullKey(); } int hash = secondaryHash(key); HashMapEntry<K, V>[] tab = table; int index = hash & (tab.length - 1); for (HashMapEntry<K, V> e = tab[index], prev = null; e != null; prev = e, e = e.next) { if (e.hash == hash && key.equals(e.key)) { if (prev == null) { tab[index] = e.next; } else { prev.next = e.next; } modCount++; size--; postRemove(e); return e.value; } } return null; }}
2.纠正误区:
现在应该能分清楚哪个更高效了吧?
第一种
Map map = new HashMap(); Iterator iter = map.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = (Map.Entry) iter.next(); Object key = entry.getKey(); Object val = entry.getValue(); }
第二种
Map map = new HashMap(); Iterator iter = map.keySet().iterator(); while (iter.hasNext()) { Object key = iter.next(); Object val = map.get(key);//要去逐个遍历链表,效率低 }
3.散列的index
还有一个很有意思的地方,经常看见这句话
index = hash & (tab.length - 1);
这是用来寻找数组中的index确定放在哪一位。那为什么要这么做呢?
1.提高效率。一般会想到用hash值对length取模(即除法散列法),但取模会用到除法运算,效率很低,&的效率高于取模
2.节省空间。length为2的整数次幂,length-1为奇数,奇数的最后一位是1,这样便保证了h&(length-1)的最后一位可能为0,也可能为1(这取决于h的值),即与后的结果可能为偶数,也可能为奇数,这样便可以保证散列的均匀性
0 0
- HashMap详解--源码分析
- Android HashMap源码详解
- Android HashMap 源码详解
- HashMap源码详解
- HashMap源码分析详解
- hashmap详解(带hashmap源码分析)
- 【集合详解】HashMap源码解析
- 基础知识(一) HashMap 源码详解
- 常用知识HashMap源码详解
- JDK8中HashMap源码详解
- Java中HashMap详解 - HashMap源码及实现原理
- 史上最详细的HashMap详解--源码分析
- HashMap源码
- HashMap源码
- HashMap源码
- hashmap 源码
- HashMap源码
- HashMap源码
- hdu1532-Drainage Ditches(最大流&EK)
- 悠悠web(1)-HTML基本结构
- 传智播客大数据第三期完整版 传智播客大数据第三期高清版两份
- 一边搭建一边理解MongoDB副本集(写偏好)
- S3C2440 LCD framebuffer 显示图像
- HashMap源码详解
- git撤销文件修改
- 1.3、if、switch、break、continue、bool、float精度和函数设计注意点
- TPS及计算方法
- Android开源—RXJava观察者设计模式
- JS常用的自定义函数addClass() insertAfter() addLoadEvent(func)
- Myeclipse中.xml文件配置错误,出现"context:property-placeholder" is not bound错误
- UISegmentedControl
- 一个完整Android项目所需要用到的gradle配置技巧