详解HashMap集合( 七 )

< 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;}说明:
对于 this.threshold = tableSizeFor(initialCapacity); 疑问解答:
tableSizeFor(initialCapacity) 判断指定的初始化容量是否是2的n次幂 , 如果不是那么会变为比指定初始化容量大的最小的2的n次幂 。 这点上述已经讲解过 。 但是注意 , 在tableSizeFor方法体内部将计算后的数据返回给调用这里了 , 并且直接赋值给threshold边界值了 。 有些人会觉得这里是一个bug,应该这样书写:this.threshold = tableSizeFor(initialCapacity) * this.loadFactor;这样才符合threshold的意思(当HashMap的size到达threshold这个阈值时会扩容) 。 但是 , 请注意 , 在jdk8以后的构造方法中 , 并没有对table这个成员变量进行初始化 , table的初始化被推迟到了put方法中 , 在put方法中会对threshold重新计算 , put方法的具体实现我们下面会进行讲解4、包含另一个“Map”的构造函数
//构造一个映射关系与指定 Map 相同的新 HashMap 。 public HashMap(Map m) {//负载因子loadFactor变为默认的负载因子0.75this.loadFactor = DEFAULT_LOAD_FACTOR;putMapEntries(m, false); }最后调用了putMapEntries , 来看一下方法实现:
final void putMapEntries(Map m, boolean evict) {//获取参数集合的长度int s = m.size();if (s > 0){//判断参数集合的长度是否大于0 , 说明大于0if (table == null)// 判断table是否已经初始化{ // pre-size// 未初始化 , s为m的实际元素个数float ft = ((float)s / loadFactor) + 1.0F;int t = ((ft < (float)MAXIMUM_CAPACITY) ?(int)ft : MAXIMUM_CAPACITY);// 计算得到的t大于阈值 , 则初始化阈值if (t > threshold)threshold = tableSizeFor(t);}// 已初始化 , 并且m元素个数大于阈值 , 进行扩容处理else if (s > threshold)resize();// 将m中的所有元素添加至HashMap中for (Map.Entry e : m.entrySet()) {K key = e.getKey();V value = http://kandian.youth.cn/index/e.getValue();putVal(hash(key), key, value, false, evict);}}}注意:
float ft = ((float)s / loadFactor) + 1.0F;这一行代码中为什么要加1.0F ?
s/loadFactor的结果是小数 , 加1.0F与(int)ft相当于是对小数做一个向上取整以尽可能的保证更大容量 , 更大的容量能够减少resize的调用次数 。 所以 + 1.0F是为了获取更大的容量 。
例如:原来集合的元素个数是6个 , 那么6/0.75是8 , 是2的n次幂 , 那么新的数组大小就是8了 。 然后原来数组的数据就会存储到长度是8的新的数组中了 , 这样会导致在存储元素的时候 , 容量不够 , 还得继续扩容 , 那么性能降低了 , 而如果+1呢 , 数组长度直接变为16了 , 这样可以减少数组的扩容 。
4.3成员方法4.3.1增加方法put方法是比较复杂的 , 实现步骤大致如下:
1)先通过hash值计算出key映射到哪个桶;
2)如果桶上没有碰撞冲突 , 则直接插入;
3)如果出现碰撞冲突了 , 则需要处理冲突:
a:如果该桶使用红黑树处理冲突 , 则调用红黑树的方法插入数据;
b:否则采用传统的链式方法插入 。 如果链的长度达到临界值 , 则把链转变为红黑树;
4)如果桶中存在重复的键 , 则为该键替换新值value;
5)如果size大于阈值threshold , 则进行扩容;
具体的方法如下:
public V put(K key, V value) {return putVal(hash(key), key, value, false, true);}说明:
1)HashMap只提供了put用于添加元素 , putVal方法只是给put方法调用的一个方法 , 并没有提供给用户使用 。所以我们重点看putVal方法 。


推荐阅读