HashMap automatic expansion of resize

In HashMap before learning conditions trigger red-black tree knowledge, there is a step resize (), come look at this piece of knowledge

HashMap is one of the most common set of JAVA, this Key-Value for storing data in the form of a key, by the internal hash table, so that the best efficiency of the access can be achieved when O (1), the actual use there may be a hash conflict, introduced the linked list and red-black tree structure, so that efficiency is not lower than the worst O (logn).

Underlying collection is based on an array, linked list data structure such a base, the collection capacity is insufficient, the dynamic expansion is triggered to ensure that there is enough space to store data, the dynamic range expansion as involve data copying, etc. to adjust, in fact, is a cumbersome time-consuming operation, it is possible to estimate and use data amount, by rational construction method, the initial capacity of the specified set, the next operation will not trigger ensure dynamic expansion.

Let's look at HashMap initialization time, have done what things

/ ** 
* Constructs AN empty <TT> the HashMap </ TT> The specified with Initial
* The default Load and Capacity factor (0.75).
*
* @Param initialCapacity The Initial Capacity.
* @Throws an IllegalArgumentException Initial Capacity The IF IS negative.
* /
public the HashMap (int initialCapacity) {
the this (initialCapacity, DEFAULT_LOAD_FACTOR);
}

the HashMap provides a method for the construction of the specified initial capacity, internal this method is actually a call to another method of construction, with a load factor constructor, the default load factor is 0.75
/**
* Constructs an empty <tt>HashMap</tt> with the specified initial
* capacity and load factor.
*
* @param initialCapacity the initial capacity
* @param loadFactor the load factor
* @throws IllegalArgumentException if the initial capacity is negative
* or the load factor is nonpositive
*/
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}

Wherein the threshold is used to store the trigger threshold HashMap expansion, that is, when the number of elements stored hashMap reaches threshold, triggers automatic expansion

As can be seen from the above method, the HashMap initialization, incoming initialCapacity not directly used, but the value after processing method tableSizeFor () obtained after a threshold

Let's look at the processing logic of this approach:

/ ** 
. * Returns Power of A GIVEN TWO The target size for Capacity
* /
static int tableSizeFor Final (int CAP) {
int n-CAP = -. 1;
n-| = n->>>. 1;
n-| = n->>> 2;
n-| = n->>>. 4;
n-| = n->>>. 8;
n-| = n->>> 16;
return (n-<0). 1: (n-?> = MAXIMUM_CAPACITY) MAXIMUM_CAPACITY: n-+. 1? ;
}

can see that in fact gradual bit operation, so that the obtained return value is always held in the N-th power of 2, for the purpose of doing so is in the subsequent expansion, the fast calculation data in the new table in the expansion position
for example purely as an external number is 10000, then after calculation, will become the minimum power = 163 842 14 multiplied by the default load factor 0.75f, without expansion, the data may be stored amount is
16384 * 0.75 = 12288 
So when initialized HashMap, incoming capacity of 10,000, the actual amount of data in storage 10000 is not triggered automatically when the expansion

then another kind of scenario, we pass an initial capacity of 1000, the situation like now? Analyze again:
the method of HashMap configuration, the expansion of the threshold value have Threhold, calculated by the direct assignment method in the constructor tableSizeFor, so if the initial incoming 1000, then by calculating the constructor obtained
threhold 1024, but at that time did not directly HashMap use, load factor at this time there is no use, when to use these parameters that do, look at the processing logic of HashMap:
/**
* Implements Map.put and related methods.
*
* @param hash hash for key
* @param key the key
* @param value the value to put
* @param onlyIfAbsent if true, don't change existing value
* @param evict if false, the table is in creation mode.
* @return previous value, or null if none
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}

In the HashMap, all data is stored by the internal array member variables table ,, after jdk1.8, table types are adjusted, but does not change the basic structure of an array, the relationship between the three is:
table .size = threshold * loadFactor
in HashMap, the table is also initialized in the resize process, i.e., resize logic method only bear the dynamic expansion, also bear the initialization table, in the above source code, it can be seen when we first
called when the put method of storing data, if found table is null, will be called to initialize resize, resize the method of adjusting the threshold value, the completion of the initialization:
/**
* Initializes or doubles table size. If null, allocates in
* accord with initial capacity target held in field threshold.
* Otherwise, because we are using power-of-two expansion, the
* elements from each bin must either stay at same index, or move
* with a power of two offset in the new table.
*
* @return the table
*/
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
E = hiTail;
}
} the while (! (E = Next) = null);
IF (! loTail = null) {
loTail.next = null;
newtab [J] = loHead;
}
IF (! hiTail = null) {
hiTail. Next = null;
newtab [J + OLDCAP] = hiHead;
}
}
}
}
}
return newtab;
}

when we specify the initial capacity, and the table has not been initialized, the time oldThr (on behalf of the expansion threshold) by threshol assignment is not 0, OLDCAP (for table capacity) = 0, so the logic would go:
IF the else (oldThr> 0) // Initial Capacity WAS Placed in threshold 
newCap = oldThr;

it will oldThr assigned to newCap, which is a newly created table will be specified when we build the capacity of HashMap value
then go down
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;


newThr = 0, will be adjusted by the load factor of loadFactor new threshold, and finally using the threshold value after loadFactor adjusted saved to the threshold and create a new array by newCap, and assign it to the table, the complete table initialization
so reinitialization when passed through initialCapacity bit operation is assigned to the threshold, but he is actually the size of the table, and eventually re-adjust the threshold by loadFactor
when an incoming initial capacity of 1,000, but said table array 1000, according to the threshold of expansion will be adjusted to 1024 * 0.75 = 768 in the resize
so when the data stored in 1000 when the lack of capacity, or will trigger an automatic expansion

so when the actual capacity when a business is initialized when, in order to avoid the automatic expansion, when will we expect capacity divided by 0.75, when the value obtained as the initial capacity.

HashMap default initial capacity is 16.

Conditions beginning, when an incoming 10000, after threshols after resize calculation 16384 12288 * 0.75 = 10000 data stored so does not trigger automatic expansion



 

Guess you like

Origin www.cnblogs.com/wangflower/p/12236481.html