Map of set details

Collection has two major interfaces: collection and Map. This article focuses on Map, another common collection type in collection.

The following is the inheritance diagram of Map:

Map introduction

The common implementation classes of Map are as follows:

  • Hashtable: a hash table implementation provided in early Java. It is thread safe and does not support null keys and values. Because its performance is not as good as concurrent HashMap, it is rarely recommended.
  • HashMap: the most commonly used hash table implementation. If there is no need for multithreading in the program, HashMap is a good choice. It supports null keys and values. If you can use ConcurrentHashMap instead in multithreading.
  • TreeMap: a Map based on the red black tree that provides sequential access. It implements the natural sorting of key s. You can also specify a Comparator to customize sorting.
  • LinkedHashMap: a subclass of HashMap, which saves the insertion order of records, and can maintain the same order as the insertion when traversing.

Common methods of Map

Common methods include: put, remove, get, size, etc. all methods are as follows:

For an example, refer to the following code:

Map hashMap = new HashMap();
// Add elements
hashMap.put("name", "Lao Wang");
hashMap.put("age", "30");
hashMap.put("sex", "Have a guess");
// Delete element
hashMap.remove("age");
// Find a single element
System.out.println(hashMap.get("age"));
// Loop all key s
for (Object k : hashMap.keySet()) {
    System.out.println(k);
}
// Loop all values
for (Object v : hashMap.values()) {
    System.out.println(v);
}

The above is an example of using HashMap, and the use of other classes is similar.

HashMap data structure

The data at the bottom of the HashMap is that the array is called a hash bucket, and each bucket stores a linked list. Each node in the linked list is each element in the HashMap. In JDK 8, when the length of the linked list is greater than or equal to 8, it will be converted into a red black tree data structure to improve the efficiency of query and insertion.

The data structure of HashMap is as follows:

Important methods of HashMap

1) Add method: put(Object key, Object value)

The execution process is as follows:

  • hash the key and calculate the storage index;
  • Determine whether there is hash collision. If there is no collision, put it directly in the hash bucket. If there is a collision, store it in the form of a linked list;
  • Determine the type of existing elements, decide whether to append the tree or the linked list. When the linked list is greater than or equal to 8, convert the linked list to a red black tree;
  • Replace the old value if the node already exists;
  • Determine whether the threshold value is exceeded, and if it is exceeded, expand the capacity.

Source code and Description:

public V put(K key, V value) {
    // Hash the key ()
    return putVal(hash(key), key, value, false, true);
}
static final int hash(Object key) {
    int h;
  // The implementation of hash() for key
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    // Create if tab is empty
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    // Calculate index and handle null
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        // Node exists
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        // The chain is a tree
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        // The chain is a list
        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;
            }
        }
        // Write in
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    // load factor*current capacity, resize exceeded
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

put() execution flow chart is as follows:

2) Get method: get(Object key)

The execution process is as follows:

  • First, compare the first node. If the hash value of the first node is the same as the hash value of the key, and the key object of the first node is the same as the key (the address is the same or the equals is the same), the node is returned;
  • If the comparison of the first node is not the same, check whether the next node exists. If so, continue the comparison. If not, it means that the key has no matching key value pair.

Source code and Description:

public V get(Object key) {
  Node<K,V> e;
  return (e = getNode(hash(key), key)) == null ? null : e.value;
}
/**
* This method is the concrete implementation of Map.get method
* Receive two parameters
* @param hash key The hash value of is addressed in the node array according to the hash value, which is obtained through the hash(key)
* @param key key Objects, when there is a hash collision, they should be compared one by one to see whether they are equal
* @return If found, the key value pair node object will be returned, otherwise null will be returned
*/
final Node<K,V> getNode(int hash, Object key) {
    Node<K,V>[] tab; Node<K,V> first, e; int n; K k; // Declare the node array object, the first node object of the linked list, the current node object during loop traversal, the array length, and the key object of the node
    // Node array assignment, array length assignment, module result obtained by bit operation to determine the first node of the linked list
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (first = tab[(n - 1) & hash]) != null) {
        if (first.hash == hash && // First, compare the first node. If the hash value of the first node is the same as the hash value of the key, and the key object of the first node is the same as the key (the address is the same or the equals is the same), the node will be returned
            ((k = first.key) == key || (key != null && key.equals(k))))
            return first; // Return to the first node

        // If the comparison of the first node is not the same, check whether the next node exists. If so, continue the comparison. If not, it means that the key has no matching key value pair    
        if ((e = first.next) != null) {
            // If the next node e exists, first check whether the first node is a tree node
            if (first instanceof TreeNode)
                // If the first node is a tree node, traverse the tree to find
                return ((TreeNode<K,V>)first).getTreeNode(hash, key); 

            // If the first node is not a tree node, it means that it is still a common linked list, then you can traverse and compare one by one    
            do {
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k)))) // Whether the hash value is the same or not, and then the address or equals
                    return e; // If the key object of current node e is the same as the key, then return e
            } while ((e = e.next) != null); // See if there is another node. If there is one, continue to the next round of comparison, otherwise jump out of the loop
        }
    }
    return null; // When the tree nodes or all linked list nodes that should be compared fail to match the key after the comparison, null will be returned

Tags: Programming Java JDK

Posted on Wed, 06 May 2020 02:16:00 -0400 by xsist10