HashMap源码解析——学习笔记
来源:互联网 发布:淘宝怎样增加收藏 编辑:程序博客网 时间:2024/05/18 18:16
hashCode() 说起
hashCode 是jdk根据对象的地址或者字符串或者数字算出来的int类型的数值
hashCode 契约
这个契约在 hashCode 方法的 JavaDoc 中进行了阐述。它可以大致的归纳为下面几点:
1.在一个运行的进程中,相等的对象必须要有相同的哈希码
2.请注意这并不意味着以下常见的误解:
3.不相等的对象一定有着不同的哈希码——错!
4.有同一个哈希值的对象一定相等——错!
不同对象的数量经常比可能的哈希吗的数量 (2^32)更大;
什么是HashMap? 主题
HashMap 是用于储存(key-value)键值对集合,每一个键值对应Entry,这些键储存在一个数据中,数据是HashMap的主干。
内部是通过数组与链表实现的,可以理解为“链表的数组”。
put操作做了什么?
public V put(K key, V value) { if (key == null) return putForNullKey(value); //计算获取hash值 int hash = hash(key.hashCode()); //获取此次hash值在数组(bucket)的下标位置 int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; 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<>(hash, key, value, e); if (size++ >= threshold) resize(2 * table.length); }
1、可以看出put储存对象时候,先调用hashCode计算hash从而得到bucket位置。
2、如果索引碰撞,遍历链表,判断是否有key存在,若存在更新value值。(不过在JDK1.8中链表元素超过8个时,会把链表结构转换成红黑树结构存储,提高查询的性能)
3.若未碰撞,直接放入表中。
4.放入前,如果元素个数超过负载因子的比例,则进行rehash,扩容,之会插入。
5.null key的元素永远会放到index为0的链表里面。
注意:当新加是达到量的阀值( threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR) ) 默认容器大小16*负载因子0.75f 则开始扩容。容量大小是之前的2倍。
hash算法是什么?
JDK1.7 static int hash(int h) { // 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); } This function ensures that hashCodes that differ only by JDK1.8 static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
当数组长度为2的n次幂的时候,不同的key算得得index相同的几率较小,那么数据在数组上分布就比较均匀,也就是说碰撞的几率小,相对的,查询的时候就不用遍历某个位置上的链表。
知识点:
当length总是2的n次方时, h & (length - 1) 等价于 hash对length取模 ,但是&比%具有更高的效率;
在n - 1为15(0x1111)时,其实散列真正生效的只是低4bit的有效位,容易碰撞;
JDK1.8
通过hashCode()的高16位异或低16位实现的:(h = k.hashCode()) ^ (h >>> 16),主要是从速度、功效、质量来考虑的,这么做可以在数组table的length比较小的时候,也能保证考虑到高低Bit都参与到Hash的计算中,同时不会有太大的开销。
modCount干什么用?
HashMap中有一个成员变量modCount,这个用来实现“fast-fail”机制(也就是快速失败)。所谓快速失败就是在并发集合中,其进行迭代操作时,若有其他线程对其结构性的修改,这是迭代器会立马感知到,当 expectedModCount!= modCount,立刻抛出ConcurrentModificationException异常,不需要等待迭代完成之后才告诉你已经出错。
get 如何实现
public V get(Object key) { if (key == null) return getForNullKey(); int hash = hash(key.hashCode()); 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.equals(k))) return e.value; } return null; } private V getForNullKey() { for (Entry<K,V> e = table[0]; e != null; e = e.next) { if (e.key == null) return e.value; } return null; }
1、判断key值是否为null,在table[0]的链表中寻找;
2、计算hash值,进而获得table中的index值
3、在table[index]的链表中寻找,根据hash值和equals()方法获得相应的value。
- HashMap源码解析——学习笔记
- HashMap源码解析笔记
- HashMap源码解析笔记
- HashMap源码学习笔记
- HashMap源码学习笔记
- HashMap源码解析——概述
- HashMap源码解析——关键属性
- HashMap源码解析——构造方法
- HashMap源码解析——put方法
- HashMap源码解析——get方法
- HashMap和HashTable源码学习笔记
- HashMap、HashSet、Hashtable源码学习笔记
- Android学习笔记—— EventBus 源码解析
- Android源码解析 -- HashMap
- HashMap源码解析
- HashMap源码解析
- HashMap源码解析
- HashMap源码解析
- Catch That Cow(BFS)
- MySQL用户变量和系统变量
- 当程序取代程序员写代码,会发生什么呢?
- 图像类型
- android Scorller使用记录
- HashMap源码解析——学习笔记
- 《kubernetes-1.8.0》05-node节点搭建
- js实现排序算法
- mint ui picker 设置初始值
- javaBean
- ffmpeg系列:使用C++类封装ffmpeg,封装读取视频帧功能Read()方法
- 煤球求和。
- leetcode解题方案--041--First Missing Positive
- IDEA调试