How and when does a HashMap initialize an entrySet and add a value to it

I find a very magical thing, simple code, as shown below:

public class Demo{ public static void main(String[] args){ HashMap<String,String> map = new HashMap<String,String>(); map.put("a", "aa"); System.out.println("end"); } } 

after calling

 HashMap<String,String> map = new HashMap<String,String>(); 

map object state the entrySet field variable is not null, that is, it has been initialized.


Then this is my first question when the entrySet was initialized? it seemed that the corresponding code should be in the HashMap construct, but below is the source code of this constructor

  public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted } 

there seemed to be no code that initializes the entrySet .

and the thing goes on. after calling

 map.put("a","aa") 

contents of the table of field variables and entrySet, as shown below. enter image description here Then this is my second problem: add this value to entrySet? It seemed like it should be a put method. and below is the put method.

 public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } 

it calls the putVal method, and below is some putVal code

 final V putVal(...) { .... tab[i] = newNode(hash, key, value, null); .... ++modCount;//after invoke this the entrySet is still empty if (++size > threshold) resize();//this has not been executed afterNodeInsertion(evict);//I debug several times, sometimes before invoke this the entrySet has an Element and sometimes return null; } 

after calling

  ++modCount; 

entrySet is empty and before the call

  afterNodeInsertion(evict); 

entrySet has an element. but it seemed that the code between the two lines had nothing with entrySet. I think maybe there are several threads that control the entrySet, then I write a small tool with jvm_ti to print threadID that calls the class below the java.util package and finds only one thread.

Then what do I miss? Is there a problem while debugging? I would like for me to clearly describe my problem and everything would be valuable.

add: my java version is 1.8.0_77 and eclipse version is 4.6.1 and 4.5.1

+5
source share
2 answers

This is your debugger that is fooling you. In debug mode, it calls toString() , which actually calls entrySet() (see AbstractMap.toString() ). This is why entrySet already initialized when you looked at it.

If you look there through reflection utilities, for example. with the following code:

 HashMap<String, String> map = new HashMap<>(); Field entrySetField = HashMap.class.getDeclaredField("entrySet"); entrySetField.setAccessible(true); Object entrySet = entrySetField.get(map); System.out.println("entrySet = " + entrySet); System.out.println("map.toString() = " + map.toString()); entrySet = entrySetField.get(map); System.out.println("entrySet = " + entrySet); 

You will get the following result:

 entrySet = null map.toString() = {} entrySet = [] 

As you can see: entrySet is actually still null if no toString() is called and initialized after it.

The same goes for your second question. If you look at the values ​​"reflexively":

 // Starting from where my entrySet is still null map.put("key", "value"); entrySet = entrySetField.get(map); System.out.println("entrySet = " + entrySet); 

You will receive, as expected:

 entrySet = null 
+6
source

A quick look at the source code shows that it stands out lazily:

 public Set<Map.Entry<K,V>> entrySet() { Set<Map.Entry<K,V>> es; return (es = entrySet) == null ? (entrySet = new EntrySet()) : es; } 

Link

I think maybe there are several threads that work on entrySet, then I write a small tool with jvm_ti to print threadID, which calls the class below the java.util package and find only one thread.

No, there are definitely NO threads involved (unless you explicitly created them) If you want to easily debug it, set watchpoint to

 transient Set<Map.Entry<K,V>> entrySet; 

inside the HashMap.

+1
source

Source: https://habr.com/ru/post/1262978/


All Articles