简介:
HashMap在java集合中的位置如图所示:
HashMap继承的是AbstractMap,而AbstractMap实现的是Map接口。
HashMap的存储方式是<key,value>键值对的形式,其中,key是唯一的,value可以重复。
基本使用方法:
put,get方法:
Map<String,Integer> m1 = new HashMap<>();
Map<String,Integer> m2 = new HashMap<>();
m1.put("AAA",1);
m1.put("bbb",2);
m2.put("ccc",3);
System.out.println(m1.get("AAA"));
System.out.println(m2.get("ccc"));
打印结果
1
3
实现原理
JDK8之前:数组+链表
JDK8之后:数组+链表+红黑树,红黑树的作用是提高查询效率
JDK8之后,如果哈希表单向链表中元素超过8个,那么单向链表这种数据结构会变成红黑树数据结构。当红黑树上的节点数量小于6个,会重新把红黑树变成单向链表数据结构。
底层源码:
可以看到源码里,put方法return的是putVal(hash(key), key, value, false, true)方法。
而继续查看putVal方法:
5个参数分别是:
hash:key的hash值
key:key值
value:key对应的value值
onlyIfAbsent:如果是true,则不改变已经存在的value值,反之改变
evict:如果为false,则表进入创建模式
首先,看628行,if判断数组tab是否为空或逻辑长度是否为0?是,就分配长度,一般为16。
630行,if判断p是否为null,p是什么?我们看到这么一句代码:
p = tab[i = (n - 1) & hash]
tab是数组,然后将(n-1)和key的hash值的&运算结果作为tab数组的下标。那么,p为数组中的某个位置或元素。如果p为空,那么说明这个位置没有k,v。那么可以新建一个Node节点(因为数组每个元素都是一个链表结构)。
如果上述两个if都不成立,即说明该位置已有元素了。再看632行else里的逻辑:
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
先if判断p位置的hash值是否等于要放入的Key的hash值并且(比较key是否==或者equal)?是的话那么将p赋值给e。
639else里:下标重复处理,关键一行代码:
p.next = newNode(hash, key, value, null);
也就是说new一个新的Node对象并把当前Node的next引用指向该对象,也就是说原来该位置上只有一个元素对象,现在转成了单向链表。
我们在上面说过,当链表长度到8时,则将链表转化为红黑树:
if (binCount >= TREEIFY_THRESHOLD - 1) //当binCount>=TREEIFY_THRESHOLD-1
treeifyBin(tab, hash);//把链表转化为红黑树
而TREEIFY_THRESHOLD的值为:
HashMap.java 258行代码
static final int TREEIFY_THRESHOLD = 8;
在Map中,一个key,对应了一个value,如果key的值已经存在,Map会直接替换value的内容,来看一下源码中是怎么实现的,来看以下代码
653行:
if (e != null) { // existing mapping for key
V oldValue = e.value;//定义一个变量来存旧值
if (!onlyIfAbsent || oldValue == null)
e.value = value;//把value的值赋值为新的值
afterNodeAccess(e);
return oldValue;//返回的值
}
未完待续…