HashMap源码理解与分析

/**
  * HashMap是常用的Java集合之一,是基于哈希表的Map接口的实现。与HashTable主要区别为不支持同步和允许null作为key和value。
  * HashMap非线程安全,即任一时刻可以有多个线程同时写HashMap,可能会导致数据的不一致。
  * 如果需要满足线程安全,可以用 Collections的synchronizedMap方法使HashMap具有线程安全的能力,或者使用ConcurrentHashMap。
  * 在JDK1.6中,HashMap采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。
  * 但是当位于一个数组中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。
  * 而JDK1.8中,HashMap采用数组+链表+红黑树实现,当链表长度超过阈值8时,将链表转换为红黑树,这样大大减少了查找时间。
  * 原本Map.Entry接口的实现类Entry改名为了Node。转化为红黑树时改用另一种实现TreeNode。
  */
 1 public class HashMap<K, V> extends AbstractMap<K, V>
 2         implements Map<K, V>, Cloneable, Serializable {
 3
 4     private static final long serialVersionUID = 362498820763181265L;
 5
 6
 7     /**
 8      * 默认的初始容量(容量为HashMap中槽的数目)是16,且实际容量必须是2的整数次幂。
 9      */
10     static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
11
12     /**
13      * 最大容量(必须是2的幂且小于2的30次方,传入容量过大将被这个值替换)
14      */
15     static final int MAXIMUM_CAPACITY = 1 << 30;
16
17     /**
18      * 默认装填因子0.75,如果当前键值对个数 >= HashMap最大容量*装填因子,进行rehash操作
19      */
20     static final float DEFAULT_LOAD_FACTOR = 0.75f;
21 /**
22      * JDK1.8 新加,Entry链表最大长度,当桶中节点数目大于该长度时,将链表转成红黑树存储;
23      */
24     static final int TREEIFY_THRESHOLD = 8;
25
26     /**
27      * JDK1.8 新加,当桶中节点数小于该长度,将红黑树转为链表存储;
28      */
29     static final int UNTREEIFY_THRESHOLD = 6;
30
31     /**
32      * 桶可能被转化为树形结构的最小容量。当哈希表的大小超过这个阈值,才会把链式结构转化成树型结构,否则仅采取扩容来尝试减少冲突。
33      * 应该至少4*TREEIFY_THRESHOLD来避免扩容和树形结构化之间的冲突。
34      */
35     static final int MIN_TREEIFY_CAPACITY = 64;
36
37  /**
38      * JDK1.6用Entry描述键值对,JDK1.8中用Node代替Entry
39      */
40     static class Node<K, V> implements Map.Entry<K, V> {
41         // hash存储key的hashCode
42         final int hash;
43         // final:一个键值对的key不可改变
44         final K key;
45         V value;
46         //指向下个节点的引用
47         Node<K, V> next;
48
49         //构造函数
50         Node(int hash, K key, V value, Node<K, V> next) {
51             this.hash = hash;
52             this.key = key;
53             this.value = value;
54             this.next = next;
55         }
56
57         public final K getKey() {
58             return key;
59         }
60
61         public final V getValue() {
62             return value;
63         }
64
65         public final String toString() {
66             return key + "=" + value;
67         }
68
69         public final int hashCode() {
70             return Objects.hashCode(key) ^ Objects.hashCode(value);
71         }
72
73         public final V setValue(V newValue) {
74             V oldValue = value;
75             value = newValue;
76             return oldValue;
77         }
78     public final boolean equals(Object o) {
79             if (o == this)
80                 return true;
81             if (o instanceof Map.Entry) {
82                 Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
83                 if (Objects.equals(key, e.getKey()) &&
84                         Objects.equals(value, e.getValue()))
85                     return true;
86             }
87             return false;
88         }
89     }
/**
  * HashMap中键值对的存储形式为链表节点,hashCode相同的节点(位于同一个桶)用链表组织
  * hash方法分为三步:
  * 1.取key的hashCode
  * 2.key的hashCode高16位异或低16位
  * 3.将第一步和第二步得到的结果进行取模运算。
  */
 1 static final int hash(Object key) {
 2         int h;
 3         //计算key的hashCode, h = Objects.hashCode(key)
 4         //h >>> 16表示对h无符号右移16位,高位补0,然后h与h >>> 16按位异或
 5         return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
 6     }
 7
 8     /**
 9      * 如果参数x实现了Comparable接口,返回参数x的类名,否则返回null
10      */
11     static Class<?> comparableClassFor(Object x) {
12         if (x instanceof Comparable) {
13             Class<?> c;
14             Type[] ts, as;
15             Type t;
16             ParameterizedType p;
17             if ((c = x.getClass()) == String.class) // bypass checks
18                 return c;
19             if ((ts = c.getGenericInterfaces()) != null) {
20                 for (int i = 0; i < ts.length; ++i) {
21                     if (((t = ts[i]) instanceof ParameterizedType) &&
22                             ((p = (ParameterizedType) t).getRawType() ==
23                                     Comparable.class) &&
24                             (as = p.getActualTypeArguments()) != null &&
25                             as.length == 1 && as[0] == c) // type arg is c
26                         return c;
27                 }
28             }
29         }
30         return null;
31     }
32
33     /**
34      * 如果x的类型为kc,则返回k.compareTo(x),否则返回0
35      */
36     @SuppressWarnings({"rawtypes", "unchecked"}) // for cast to Comparable
37     static int compareComparables(Class<?> kc, Object k, Object x) {
38         return (x == null || x.getClass() != kc ? 0 :
39                 ((Comparable) k).compareTo(x));
40     }
41
42     /**
43      * 结果为>=cap的最小2的自然数幂
44      */
45     static final int tableSizeFor(int cap) {
46         //先移位再或运算,最终保证返回值是2的整数幂
47         int n = cap - 1;
48         n |= n >>> 1;
49         n |= n >>> 2;
50         n |= n >>> 4;
51         n |= n >>> 8;
52         n |= n >>> 16;
53         return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
54     }
/* ---------------- Fields -------------- */
 1  /**
 2      * 哈希桶数组,分配的时候,table的长度总是2的幂
 3      */
 4     transient Node<K, V>[] table;
 5
 6     /**
 7      * HashMap将数据转换成set的另一种存储形式,这个变量主要用于迭代功能
 8      */
 9     transient Set<Map.Entry<K, V>> entrySet;
10
11     /**
12      * 实际存储的数量,则HashMap的size()方法,实际返回的就是这个值,isEmpty()也是判断该值是否为0
13      */
14     transient int size;
15
16     /**
17      * hashmap结构被改变的次数,fail-fast机制
18      */
19     transient int modCount;
20
21     /**
22      * HashMap的扩容阈值,在HashMap中存储的Node键值对超过这个数量时,自动扩容容量为原来的二倍
23      *
24      * @serial
25      */
26     int threshold;
27
28     /**
29      * HashMap的负加载因子,可计算出当前table长度下的扩容阈值:threshold = loadFactor * table.length
30      *
31      * @serial
32      */
33     final float loadFactor;

具体源码参考地址:

https://github.com/wupeixuan/JDKSourceCode1.8/blob/master/src/java/util/HashMap.java

原文地址:https://www.cnblogs.com/huststl/p/8628765.html

时间: 2024-10-21 15:11:57

HashMap源码理解与分析的相关文章

HashMap源码理解

导语 HashMap是常用的数据结构,了解HashMap,对提高代码的效率有很大的帮助.HashMap在JDK1.8中对数据结构进行了优化:提高了查询和删除的效率.当然,这也导致了结构更加的复杂:但通过认真阅读源码,还是可以掌握其要领的. 读完本篇文章,你应该理解的内容 点击这里查看大图 说明:HashMap的数据结构是个Hash表(可以理解为数组),每个槽中存放着一些节点. 一般情况下,一个槽中存放一个节点: 数据量较大时,一个槽中可能存放多个节点,此时,各个节点以链表的方式连接在一起: 当一

[Java] HashMap源码分析

1.概述 Hashmap继承于AbstractMap,实现了Map.Cloneable.java.io.Serializable接口.它的key.value都可以为null,映射不是有序的. Hashmap不是同步的,如果想要线程安全的HashMap,可以通过Collections类的静态方法synchronizedMap获得线程安全的HashMap. Map map = Collections.synchronizedMap(new HashMap()); (除了不同步和允许使用 null 之

HashMap源码分析(转载)

一.HashMap概述 HashMap基于哈希表的 Map 接口的实现.此实现提供所有可选的映射操作,并允许使用 null 值和 null 键.(除了不同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同.)此类不保证映射的顺序,特别是它不保证该顺序恒久不变. 值得注意的是HashMap不是线程安全的,如果想要线程安全的HashMap,可以通过Collections类的静态方法synchronizedMap获得线程安全的HashMap. Map map = Coll

Java中HashMap源码分析

一.HashMap概述 HashMap基于哈希表的Map接口的实现.此实现提供所有可选的映射操作,并允许使用null值和null键.(除了不同步和允许使用null之外,HashMap类与Hashtable大致相同)此类不保证映射的顺序,特别是它不保证该顺序恒久不变. 值得注意的是HashMap不是线程安全的,如果想要线程安全的HashMap,可以通过Collections类的静态方法synchronizedMap获得线程安全的HashMap. Map map = Collections.sync

Java进阶之----HashMap源码分析

今天我们接着来看HashMap的源码,对几个常用的方法进行分析.在分析之前,我们还是要先对HashMap的结构有一个了解.看过之前我分析的ArrayList和LinkedList源码的朋友应该清楚,ArrayList内部是以数组实现的,LinkedList内部是以链表实现的.而HashMap则是对数组和链表的结合,虽然看上去复杂了一些,不过仔细分析一下,还是很好理解的.我们来看一张图片,是我根据我的理解画的. 我们在来看看Entry的内部结构是什么: 以上两个图,相信大家对HashMap的结构有

HashMap源码分析 (JDK1.7)

看HashMap源码有一段时间了,但是一直没有写点什么,这几天趁着要换实习公司,没什么事做,就把自己对HashMap的理解写下来,边写边整理自己的思路. 这是借用别人画的理解HashMap的图,简单理解就是它结合了数组查找快和链表插入删除快的优势. 下面直接分析源码: 先从构造函数说起: public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumen

[java源码解析]对HashMap源码的分析(二)

上文我们讲了HashMap那骚骚的逻辑结构,这一篇我们来吹吹它的实现思想,也就是算法层面.有兴趣看下或者回顾上一篇HashMap逻辑层面的,可以看下HashMap源码解析(一).使用了哈希表得"拉链法". 我打算按这个顺序来讲HashMap:几个关键属性 -> 构造方法-> 存取元素方法 ->解决hash冲突方法->HashMap扩容问题. 4个关键属性: /** *HashMap的存储大小 */ transient int size; /** * HashMa

【JAVA集合】HashMap源码分析(转载)

原文出处:http://www.cnblogs.com/chenpi/p/5280304.html 以下内容基于jdk1.7.0_79源码: 什么是HashMap 基于哈希表的一个Map接口实现,存储的对象是一个键值对对象(Entry<K,V>): HashMap补充说明 基于数组和链表实现,内部维护着一个数组table,该数组保存着每个链表的表头结点:查找时,先通过hash函数计算hash值,再根据hash值计算数组索引,然后根据索引找到链表表头结点,然后遍历查找该链表: HashMap数据

HashMap源码分析(基于JDK1.6)

在Java集合类中最常用的除了ArrayList外,就是HashMap了.本文尽自己所能,尽量详细的解释HashMap的源码.一山还有一山高,有不足之处请之处,定感谢指定并及时修正. 在看HashMap源码之前先复习一下数据结构. Java最基本的数据结构有数组和链表.数组的特点是空间连续(大小固定).寻址迅速,但是插入和删除时需要移动元素,所以查询快,增加删除慢.链表恰好相反,可动态增加或减少空间以适应新增和删除元素,但查找时只能顺着一个个节点查找,所以增加删除快,查找慢.有没有一种结构综合了