JDK8 HashMap source putMapEntries resolve

putMapEntries function will be the copy constructor HashMap public HashMap(Map<? extends K, ? extends V> m)or Map interface putAllfunction (HashMap to be achieved) to the call. Because this function is the default package access, so users under normal circumstances can not be invoked.

putMapEntries full resolution

final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {//m的类型参数是? extends,所以只能使用泛型代码的出口,比如get函数
    int s = m.size();
    if (s > 0) {//前提是传入map的大小不为0,
        if (table == null) { // 说明是拷贝构造函数来调用的putMapEntries,或者构造后还没放过任何元素
            //先不考虑容量必须为2的幂,那么下面括号里会算出来一个容量,使得size刚好不大于阈值。
            //但这样会算出小数来,但作为容量就必须向上取整,所以这里要加1
            float ft = ((float)s / loadFactor) + 1.0F;
            //如果小于最大容量,就进行截断;否则就赋值为最大容量
            int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                     (int)ft : MAXIMUM_CAPACITY);
            //虽然上面一顿操作猛如虎,但只有在算出来的容量t > 当前暂存的容量(容量可能会暂放到阈值上的)时,才会用t计算出新容量,再暂时放到阈值上
            if (t > threshold)
                threshold = tableSizeFor(t);
        }
        //说明table已经初始化过了;判断传入map的size是否大于当前map的threshold,如果是,必须要resize
        //这种情况属于预先扩大容量,再put元素
        else if (s > threshold)
            resize();
        //循环里的putVal可能也会触发resize
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {//下面的Entry泛型类对象,只能使用get类型的函数
            K key = e.getKey();
            V value = e.getValue();
            putVal(hash(key), key, value, false, evict);
        }
    }
}

Notes had already been explained very clearly, here, to mention several important.

  • if (table == null)Branch instructions are putMapEntries HashMap copy constructor to call, or after the structure has not let any element, then call putAll.
  • float ft = ((float)s / loadFactor) + 1.0F1 is added here because, size / loadFactor = capacity, but if the capacity is calculated fractional, but rounded down, will cause a large capacity is not enough, therefore, if the fractional capacity, must be rounded up.
  • Calculated capacity must be less than maximum capacity MAXIMUM_CAPACITY, or directly to capacity equal MAXIMUM_CAPACITY.
  • if (t > threshold)Member threshold value here is the value of the actual storage capacity of. Because in the table has not been initialized (table or null), the user will be given the capacity to staging threshold members go (after all, not a member, called HashMap capacity, capacity as the size of the table array implicitly exists).
  • else if (s > threshold)Description Incoming map of size have been greater than the threshold current map, and that is certainly not fit the current map and two map sets, so there must be performed resize operation.
  • In the last cycle putValmay also trigger resize operation.

About float ft = ((float) s / loadFactor) + 1.0F

Finally, this code plus one operation although I have already explained, but it is also possible to have "harm" the. The following code, if you debug, will see the following:

import java.util.*;

public class test1 {
    public static void main(String[] args) {
        HashMap<String,Integer> oldMap = new HashMap<String,Integer>();
        for(int i=0;i<12;i++){
            oldMap.put(""+i,i);
        }
        HashMap<String,Integer> newMap = new HashMap<String,Integer>(oldMap);
        System.out.println();//此处打断点
    }
}

Here Insert Picture Description

  • oldMap used, no arguments constructor (the default values ​​will be used to the HashMap), the capacity is 16, the load factor is 0.75, the threshold is 12. The copy constructor newMap used, then calls putMapEntries, passed since the map size is 12, then 12 / 0.75 = 16, 16 + 1 = 17, tableSizeFor (17) = 32, so the final result is the capacity of 32 newMap , the threshold is 24.
  • This is what I say "bad", newMap the size and size oldMap obviously the same, but its capacity and threshold values ​​are twice the oldMap (becomes due twice).
  • You may be thinking that this is another special case in order to protect this special case if we did not add 1 operation, will lead to allocation of capacity is not big enough, so I have to sacrifice a little longer this special case. But even if you put the value of s (the incoming map size) 12, also found from the test to 6, even if an operation is not added, the capacity will be allocated large enough.
  • In fact, we missed the point, the user is loadfactor might be strange to a decimal because HashMap, the capacity must be a power of 2, and the default loadfactor is 0.75, so the calculated threshold must be an integer up. If the user to a decimal, making capacity * loadfactor = decimal, then the threshold value must be rounded down (the other way round, if the threshold value is rounded up, would not it make size may be larger than the true threshold without resize, see resize()function newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE);, where (int)ftis rounded down). So the other way round, with the size / loadfactor calculated capacity, will almost certainly be rounding up the.

关于else if (s > threshold) resize()

This code fully embodies the HashMap's "lazy mode" is an extremely expensive because the resize operation should be done only when needed.

  • When s> time threshold, the number of mapping incoming map and the current map of the union (ie size) will certainly be greater than the current threshold value map is, so the cycle to place the new element (the last cycle putValbefore the operation) should resize, because we already know in advance is not enough to put the current map.
  • When s <= threshold, if this determination will not enter, will not resize the. This is because, if the incoming map is a subset of the current map, then you certainly do not need to resize the up. But this is an extreme case, so here's task to trigger resize putVal. In short, this is a final or need to resize uncertain thing.
Published 171 original articles · won praise 130 · views 280 000 +

Guess you like

Origin blog.csdn.net/anlian523/article/details/103639094