HashMap
Posted jin1000x
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HashMap相关的知识,希望对你有一定的参考价值。
HashMap继承了AbstractMap抽象类,实现了Map,Cloneable,Serializable接口。因此HashMap集合元素是一对键值:K-V,K元素不可重复(Key重复会覆盖value)且如果为null,必须要放在第一位且只能有一个null,V元素可空可重复。
HashMap底层是有数组+链表+红黑树构成的。当一个索引位置的节点在增加到达9个时,会触发链表节点(Node)转红黑树节点(TreeNode,间接继承Node),转成红黑树节点后,其实链表的结构还存在,通过next属性维持。链表节点转红黑树节点的具体方法为源码中的treeifyBin(Node<K,V>[] tab, int hash)方法。当一个索引位置的节点删除达到6个时,并且该索引位置的节点为红黑树节点,会触发红黑树节点转链表节点。红黑树节点转链表节点的具体方法为源码中的untreeify(HashMap<K,V> map)方法。
new HashMap()对象,如果不指定集合大小,则默认初始化大小为(capacity)16(这个可以在put元素时调用resize()看到),负载因子为(loadFactor):0.75。同时当存放元素达到capacity*loadFactor时,就会发生扩容,每次扩容为原来的2倍。HashMap是非线程安全的,在并发场景下使用ConcurrentHashMap来代替。HashMap在JDK1.8之后不再有死循环的问题,JDK1.8之前存在死循环的根本原因是在扩容后同一索引位置的节点顺序会反掉。
同时HashMap也是无序的,因为HashMap它的顺序是基于HashCode,HashCode是一个随机性很强的数字,所以HashMap中的Entry完全是随机存放的。
HashMap在1.7之前使用的是数组+链表实现,在1.8+使用的数组+链表+红黑树实现。
HashMap的table为什么是transient修饰的?
(1) transient 是表明该数据不参与序列化。因为 HashMap 中的存储数据的数组数据成员中,数组还有很多的空间没有被使用,没有被使用到的空间被序列化没有意义。所以需要手动使用 writeObject() 方法,只序列化实际存储元素的数组。
(2) 从源码中知道,table中元素位置是由hashCode决定的,由于不同的虚拟机对于相同 hashCode 产生的 Code 值可能是不一样的,导致反序列的值会不同,产生差异。
HashMap和Hashtable的区别:
HashMap允许key和value为null,Hashtable不允许。
HashMap的默认初始容量为16,Hashtable为11。
HashMap的扩容为原来的2倍,Hashtable的扩容为原来的2倍加1。
HashMap是非线程安全的,Hashtable是线程安全的。
HashMap的hash值重新计算过,Hashtable直接使用hashCode。
HashMap去掉了Hashtable中的contains方法。
HashMap继承自AbstractMap类,Hashtable继承自Dictionary类。
以上是关于HashMap的主要内容,如果未能解决你的问题,请参考以下文章