1. Preface
If you can use the diagram clearly, you will not use the code firmly. If you can clear the code, do not write the explanation (not to write the comments OH).
All of the following are for JDK 1.7 and previous HashMap only.
2. Data structure
HashMap internal by maintaining a entry<k, v> array (variable is table) to achieve its basic functions, and entry<k, V> is the inner class of HashMap, its main role is to store key value pairs, the data structure is roughly as shown.
From the entry data structure can be seen, multiple entry can form a one-way list, hashmap maintenance entry<k, v> Array (hereafter referred to as entry array, or table, easy to distinguish) is actually a series of storage entry< K, v> the table header of the linked list. Then the data structure of the table array stored in the HashMap can be roughly as shown (assuming only partial data).
Note: The default length of the entry array is 16 and the load factor is 0.75.
Each row in the Bucketindex is called a bucket, so the index of the table is a. In HashMap, the operation of table and bucket (bucket) is the most important in the operation of inserting, obtaining and deleting. The following will be mainly through the insert operation to see its data structure changes.
3. Insert
For the data structure in, the insert operation is to insert the key-value (Key-value) to calculate the hash value based on key to select a specific storage location.
The source code for the Insert function is as follows (mark or Chinese note, non-JDK source comment, hereinafter):
PublicVput(K key, V value) {//Mark A Begin if(table = = empty_table) {inflatetable (threshold); }if(Key = =NULL)returnPutfornullkey (value);//Mark A End inthash = hash (key);//Calculate hash value inti = indexfor (hash, table.length);//Calculate the position index of the bucket (bucketindex) //Mark B begin for(entry<k,v> e = table[i]; E! =NULL; E = E.next) {Object k;if(E.hash = = Hash && (k = e.key) = = Key | | key.equals (k))) {V oldValue = E.value; E.value = value; E.recordaccess ( This);returnOldValue; } }//Mark B Endmodcount++;//Record the number of changes, and the iteration will determine whether or not it has been modifiedAddEntry (hash, key, value, I);return NULL;}
In the above code, the main function of the code snippet a (Mark a begin-mark a End, the same below) is to initialize the array if the table is empty, and insert the operation when the key is null, and code snippet B overwrites the original value when inserting the same key and returns the original value. The focus here is on the addentry (hash, key, value, I) method.
AddEntry method source code is as follows:
void addEntry(intint bucketIndex) { if ((size >= threshold) && (null != table[bucketIndex])) { // 扩充table数组的大小 resize(2 * table.length); // 重新计算hash值 hash = (null0; // 重新计算桶的位置索引 bucketIndex = indexFor(hash, table.length); } createEntry(hash, key, value, bucketIndex);}
Createentry method source code is as follows:
void createEntry(intint bucketIndex) { Entry<K,V> e = table[bucketIndex]; // 将新的Enrty元素插入到对应桶的表头 new Entry<>(hash, key, value, e); size++;}
Entry<> instantiation of the source code is as follows:
Entry(int h, K k, V v, Entry<K,V> n) { value = v; // 将原先桶的表头向后移动 key = k; hash = h;}
In the entire insert operation, there is a very important operation, that is, the table array expansion, the expansion of the algorithm is relatively simple, but in multi-threaded it is easy to raise a thread-safe problem.
Note: expansion requires that the values in the original table be moved to a new array and then assigned to the table variable, with an appropriate initial size and load factor to improve efficiency.
4. Thread is not secure
In a multithreaded environment, assume that there is a container map, which stores the situation as shown (light blue is the existing data).
At this point, the map has reached the expansion threshold of 12 (16 * 0.75 = 12), and thread A and thread B at the same time the map container to insert operations, then need to expand. Here's what might happen: both thread A and thread B are expanding, and there are two new table, and then when you assign to the original table variable, one of the newtable will be overwritten. If the newtable of thread B expands to cover the newtable of thread A's expansion, and after a has performed an insert operation, then the insertion failure of thread A will occur, that is, the two table can only have one last, and one of the inserted values will be discarded.
This is HashMap thread insecurity, and of course this is just one of them. To eliminate this vulnerability, you can lock or use thread-safe classes such as Hashtable and Concurrenthashmap, but Hashtable is not recommended, and the Concurrenthashmap container is the recommended use.
The basic principle of "schematic jdk source" hashmap and its thread safety