one

What are the outputs of the following codes?

Code 1

        Map<Map<String, String>, String> map = new HashMap<>();
        HashMap<String, String> key = new HashMap<>();
        map.put(key, "value");
        System.out.println("Before:" + map.size());
        Iterator<Map.Entry<Map<String, String>, String>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            iterator.next();
            iterator.remove();
        }
        System.out.println("After:" + map.size());
Copy the code

Code 2

        Map<Map<String, String>, String> map = new HashMap<>();
        HashMap<String, String> key = new HashMap<>();
        map.put(key, "value");
        key.put("1"."2");
        System.out.println("Before:" + map.size());
        Iterator<Map.Entry<Map<String, String>, String>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            iterator.next();
            iterator.remove();
        }
        System.out.println("After:" + map.size());
Copy the code

Code 3

        Map<Map<String, String>, String> map = new HashMap<>();
        HashMap<String, String> key = new HashMap<>();
        map.put(key, "value");
        key.put("1"."1");
        System.out.println("Before:" + map.size());
        Iterator<Map.Entry<Map<String, String>, String>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            iterator.next();
            iterator.remove();
        }
        System.out.println("After:" + map.size());
Copy the code

The second

Here we go. The output is

// code 1 Before:1 After:0 // code 2 Before:1 After:1Copy the code

Why is that? If you’re like me, it’s a little confusing. Well, let’s take our time to find out.

The third

Hashmap.entryset ().iterator() returns a HashIterator instance. Then navigate to the remove method

        public final void remove() {
            Node<K,V> p = current;
            if (p == null)
                throw new IllegalStateException();
            if(modCount ! = expectedModCount) throw new ConcurrentModificationException(); current = null; K key = p.key; removeNode(hash(key), key, null, false.false);
            expectedModCount = modCount;
        }
Copy the code

You’ll see that he actually calls the HashMap::removeNode method. Follow up the method

    final Node<K,V> removeNode(int hash, Object key, Object value,
                               boolean matchValue, boolean movable) {
        Node<K,V>[] tab; Node<K,V> p; int n, index;
        if((tab = table) ! = null && (n = tab.length) > 0 && (p = tab[index = (n - 1) &hash]) != null) {
               // remove node .....
        }
        return null;
    }
Copy the code

With DEBUG mode enabled, we can see that in code 2, the program is executing (p = TAB [index = (n-1) & hash])! = null, the result is null. That means no element is found in the desired storage location.

Let’s look at how HashMap handles new elements. Find the HashMap::put method, which looks like this:

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); / /... some operate......return something;
}
Copy the code

At this point, we’re done. The original HashMap places elements based on the target’s HashCode. After that, if you modify the original object by reference, the element will not be found at remove.

Its 4

Those careful students may have noticed that in block 3, we also made changes to the original object and still managed to remove it.

The HashCode implementation of a HashMap

    public int hashCode() {
        int h = 0;
        Iterator<Entry<K,V>> i = entrySet().iterator();
        while (i.hasNext())
            h += i.next().hashCode();
        return h;
    }
Copy the code

Here is the sum of the hashcodes of all the elements in the collection. Continue tracing to find hashmap. Node::hashCode

        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }
Copy the code

A Node HashCode is a HashCode with a key and a value. After we put(“1”, “1”), the HashCode of the HashMap is still 0. The HashCode before and after the value is the same, 0. At this time to Remove, of course, can be successful.

The final

This does not only affect the remove of Iterator. It also affects the remove, get, and other methods of the HashMap.

To sum up. The element location of a HashMap is related to the HashCode of its Key. In daily development, Immuable objects should be used as Key as possible. Otherwise, reading and writing to the collection will fail.