Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Infinite loop when using a key multiple times in HashMap

HashMap falls into an infinite loop.

I am not able to understand why HashMap throws stackoverflow error when the same key is used multiple times.

Code:

import java.util.HashMap;

public class Test {
    public static void main(String[] args) {
        HashMap hm = new HashMap();

        hm.put(hm, "1");
        hm.put(hm, "2");
    }
} 

Error:

Exception in thread "main" java.lang.StackOverflowError

like image 236
Sachin Sachdeva Avatar asked Jul 04 '16 10:07

Sachin Sachdeva


People also ask

Can HashMap cause infinite loop?

The default capacity of HashMap is 16 and Load factor is 0.75, which means HashMap will double its capacity when 12th Key-Value pair enters in the map (16 * 0.75 = 12). When 2 thread tries to access HashMap simultaneously, then you may encounter infinite loop.

Do Hashmaps allow for duplicates?

HashMap stores key, value pairs and it does not allow duplicate keys. If the key is duplicate then the old key is replaced with the new value.

What's wrong using HashMap in the multi threaded environment when get () method go to the infinite loop?

What's wrong using HashMap in multithreaded environment? When get() method go to infinite loop? It is a bug to have multiple threads use a non-synchronized collection (really any mutable class) in an unprotected manner. Certain if each thread had their own HashMap instance then this is not an issue.

Can we store multiple values for a key in HashMap?

HashMap can be used to store key-value pairs. But sometimes you may want to store multiple values for the same key. For example: For Key A, you want to store - Apple, Aeroplane.


2 Answers

It is not possible to add to a Map itself as a key. From javadoc:

A special case of this prohibition is that it is not permissible for a map to contain itself as a key.


The problem is that you are using as key not a standard object (any object with well defined equals and hashCode methods, that is not the map itself), but the map itself.

The problem is on how the hashCode of the HashMap is calculated:

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

As you can see, to calculate the hashCode of the map, it iterates over all the elements of the map. And for each element, it calculates the hashCode. Because the only element of the map has as key is the map itself, it becomes a source of recursion.

Replacing the map with another object that can be used as key (with well defined equals and hashCode) will work:

import java.util.HashMap;

 public class Test {
   public static void main(String[] args) {
     HashMap hm = new HashMap();

     String key = "k1";
     hm.put(key, "1");
     hm.put(key, "2");
   }
} 
like image 52
Davide Lorenzo MARINO Avatar answered Oct 06 '22 22:10

Davide Lorenzo MARINO


Problem is not that hash map blows up the stack for "same key" entered twice, but because your particular choice of map key. You are adding hash map to itself.

To explain better - part of Map contract is that keys must not change in a way that affects their equals (or hashCode for that matter) methods.

When you added map to itself as a key, you changed key (map) in a way that is making it return different hashCode than when you first added map.

For more information this is from JDK dock for Map interface:

Note: great care must be exercised if mutable objects are used as map keys. The behavior of a map is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is a key in the map. A special case of this prohibition is that it is not permissible for a map to contain itself as a key. While it is permissible for a map to contain itself as a value, extreme caution is advised: the equals and hashCode methods are no longer well defined on such a map.

like image 30
river Avatar answered Oct 06 '22 23:10

river