刨析源码,深层讲解 Java-集合框架( 九 )


为false,则仍可添加成功 。指向原有的entry元素 。
? 的扩容 :
当中的元素越来越多的时候,hash冲突的几率也就越来越高,因为数组的长度是固定的 。所以为了提高查询的效率,就要对的数组进行扩容,而在数组扩容之后,最消耗性能的点就出现了:原数组中的数据必须重新计算 其在新数组中的位置,并放进去,这就是 。
? 那么什么时候进行扩容呢?
当中的元素个数超过数组大小(数组总大小,不是数组中个数
size) 时,就会进行数组扩容,的默认值()为0.75,这是一个折中的取值 。也就是说,默认情况下,数组大小(CITY)为16,那么当中元素个数超过160.75=12(这个值就是代码中的值,也叫做临界值)的时候,就把数组的大小扩展为
2*16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知中元素的个数,那么预设元素的个数能够有效的提高的性能 。
? 的内部存储结构其实是数组+链表+树的结合 。
当实例化一个时,会初始化和,在put第一对映射关系时,系统会创建一个长度为的Node数组,这个长度在哈希表中被称为容量(),在这个数组中可以存放元素的位置我们称之为“桶”(),每个都有自己的索引,系统可以根据索引快速的查找中的元素 。
? 每个中存储一个元素,即一个Node对象,但每一个Node对象可以带一个引用变量next,用于指向下一个元素,因此,在一个桶中,就有可能生成一个Node链 。也可能是一个一个对象,每一个对象可以有两个叶子结点left和right,因此,在一个桶中,就有可能生成一个树 。而新添加的元素作为链表的last,或树的叶子结点 。
? 那么什么时候进行扩容和树形化呢?
当中的元素个数超过数组大小(数组总大小,不是数组中个数
size) 时,就会进行数组扩容,的默认 值
()为0.75,这是一个折中的取值 。也就是说,默认情况下,数组大小(CITY)为16,那么当中元素个数超过160.75=12(这个值就是代码中的值,也叫做临界值) 的时候,就把数组的大小扩展为
2*16=32,即扩大一倍,然后重新计算每个元 素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知中元素的个数,那么预设元素的个数能够有效的提高的性能 。
? 当中的其中一个链的对象个数如果达到了8个,此时如果没有
达到64,那么会先扩容解决,如果已经达到了64,那么这个链会变成树,结点类型由Node变成类型 。当然,如果当映射关系被移除后,下次方法时判断树的结点个数低于6个,也会把树再转为链表 。
? 关于映射关系的key是否可以修改?:不要修改
映射关系存储到中会存储key的hash值,这样就不用在每次查找时重新计算
每一个Entry或Node()的hash值了,因此如果已经put到Map中的映射关系,再修改key的属性,而这个属性又参与值的计算,那么会导致匹配不上 。
? 总结:
map = new ();//默认情况下,先不创建长度为16的数组
当首次调用map.put()时,再创建长度为16的数组
数组为Node类型,在jdk7中称为Entry类型
形成链表结构时,新添加的key-value对在链表的尾部(七上八下)
当数组指定索引位置的链表长度>8时,且map中的数组的长度> 64时,此索引位置 上的所有key-value对使用红黑树进行存储 。
? 面试题:负载因子值的大小,对有什么影响?
负载因子的大小决定了的数据密度 。
负载因子越大密度越大,发生碰撞的几率越高,数组中的链表越容易长,造成查询或插入时的比较次数增多,性能会下降 。