赞
踩
hashMap是基于哈希表的Map接口的非同步实现。次实现提供所有可选的映射操作,并允许使用num值和null键。此类不保证映射的顺序,特别是它不保证该顺序更久不变。
DEFAULT_INITIAL_CAPACITY : HashMap的默认容量,16
MAXIMUM_CAPACITY : HashMap的最大支持容量,2^30
DEFAULT_LOAD_FACTOR :HashMap的默认加载因子
TREEIFY_THRESHOLD :Bucket中链表长度大于该默认值,转化为红黑树
UNTREEIFY_THRESHOLD :Bucket中红黑树存储的Node小于该默认值,转化为链表
MIN_TREEIFY_CAPACITY :桶中的Node被树化时最小的hash表容量。(当桶中Node的
数量大到需要变红黑树时,若hash表容量小于MIN_TREEIFY_CAPACITY时,此时应执行
resize扩容操作这个MIN_TREEIFY_CAPACITY的值至少是TREEIFY_THRESHOLD的4
倍。)
table :存储元素的数组,总是2的n次幂
entrySet: :存储具体元素的集
size :HashMap中存储的键值对的数量
modCount :HashMap扩容和结构改变的次数。
threshold :扩容的临界值,=容量*填充因子
loadFactor: :填充因子
当实例化一个HashMap时,系统会创建一个长度为Capatity的Entity数组,这个长度在哈希表纵被称为容量(Capacity),在这个数组中可以存放的元素的位置我们称之为“桶”(bucket), 每个桶都有自己的索引,系统可以根据索引快速的查找bucket中的元素。
每个bucket中存储一个元素,即一个Entry对象,但每一个Entry对象可以带一个引用变量,用于指向下一个元素,因此,砸一个桶中,就有可能生成一个Entry链。而且新添加的元素作为链表的head。
向HashMap中添加entry1(key,value),需要首先entry1中key的哈希值(根据key所在类的hashCode()计算得到),此哈希值经过处理(哈希长度对Capacity进行取余,得到的余数为在数组要存储的位置)以后,得到在底层Entry[]数组中要存储的位置 i 。如果位置 i 上没有元素,则entry1直接添加成功。如果位置 i 上已经存在entry2(或还有链表存在的entry3,entry4),则需要通过循环的方法,一次比较entry1中key和其他的entry。如果彼此哈希值不同,则直接添加成功。如果hash值不同,继续比较二者是否equals。如果返回值为true,则使用entry1的value去替换equals为true的entry的value。如果遍历一遍后,发现所有的equals返回都为false,则entry1仍可添加成功。entry1指向原有的entry元素。
当HashMap中的元素越来越多的时候,hash冲突的几率也就越来越高,因为数组的长度是固定的。所以为了提高查询的效率,就要对HashMap的数组进行扩容,而在HashMap数组扩容之后,最消耗性能的点就出现了:原数组中的数据必须重新计算在新数组的位置,并放进去,这就是resize。
那么HashMap什么时候进行扩容呢?
当HashMap中的元素个数超过数组大小(数组总大小length,不是数组中个数size)* loadFactor时,就会进行数组扩容,loadFactor的默认值(DEFAULT_LOAD_FACOTR)为0.75,这是一个折中的取值。也就是说,默认情况下数组大小(DEFAULT_INITIAL_CAPACITY)为16,那么当HashMap中元素个数超过160.75=12(这个值就是代码中的threshold值,也叫作临界值)的时候,就把数组的大小扩展为216=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这时一个非常消耗性能的操作,所以如果我们已经预知HashMap中的元素个数,那么预设元素的个数能够有效的提高HashMap的性能。
static final int TREEIFY_THRESHOLD = 8 变成树型结构的临界值为 8
static final int UNTREEIFY_THRESHOLD = 6 恢复链式结构的临界值为 6
当哈希表的大小超过这个阈值,才会把链式结构转化成树型结构,否则仅采取扩容来尝试减少冲突
static final int MIN_TREEIFY_CAPACITY = 64;
HashMap的内部存储结构是数组+链表+红黑树**。当实例化一个HashMap时,会初始化initialCapatity和loadFactor**,在put第一对映射关系时,系统会创建一个长度为initialCapatity的Node数组,这个长度在哈希表中被称为容量(Capacity),在这个数组中可以存放元素的位置我们称之为“桶”(bucket),每个bucket都有自己的索引,系统可以根据索引快速的查找bucket中的元素。**
每个bucket中存储一个元素,即Node对象,但每一个Node对象可以带一个引用变量next,用于指向下一个元素,因此,一个桶中,就有可能生成一个Node链。也可能是以一个一个TreeNode对象,每一个TreeNode对象可以有两个叶子节点left和right,因此,在一个桶中,就有可能生成一个TreeNode树。而新添加的元素座位链表的last,或树的叶子节点。
当HashMap中的元素个数超过数组大小(数组总大小length,不是数组中个数size)loadFactor 时 , 就 会 进 行 数 组 扩 容 , loadFactor 的 默 认 值(DEFAULT_LOAD_FACTOR)为0.75,这是一个折中的取值。也就是说,默认情况下,数组大小(DEFAULT_INITIAL_CAPACITY)为16,那么当HashMap中元素个数超过160.75=12(这个值就是代码中的threshold值,也叫做临界值)的时候,就把数组的大小扩展为 2*16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知HashMap中元素的个数,那么预设元素的个数能够有效的提高HashMap的性能。
当HashMap中的其中一个链的对象个数如果达到了8个,此时如果capacity没有达到64,那么HashMap会先扩容解决,如果已经达到了64,那么这个链会变成树,节点类型由Node变成TreeNode类型。当然,如果当映射关系被移除后,下次resize方法时判断树的节点个数低于6个,也会把树转为链表。
映射关系存储到HashMap中会存储key的hash值,这样就不用在每次查找时重新计算每一个Entry或Node(TreeNode)的hash值了,因此如果已经put到Map中的映射关系,再修改key的属性,而这个属性又参与hashcode值的计算,那么会导致匹配不上。
1.HashMap map= new HashMap();默认情况下,先不创建长度为16的数组。**JDK7中:随实例化中创建。
2.当首次调用map.put()时,再创建长度为16的数组。
3.数组为Node类型,在JDK7中:**为Entry类型。
4.形成链表结构时,新添加的key-value对在链表的尾部。**JDK7中:**新加入的放在链头,最先加入的放在链尾。
5.当数组指定索引位置的链表长度>8时,且map中的数组长度>64时,此索引位置上的所有key-value对使用红黑树进行存储。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。