Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how and when HashMap initialize entrySet and add value into it

Tags:

java

hashmap

I find a very magic thing, the simple code as 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 invoke

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

the state of map object the field variable entrySet is not null, that is to say it have been initialize.


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

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

it seemed there dont exist code which initialize entrySet.

and thing go on. after invoke

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

the content of field variable table and entrySet as below shot. enter image description here Then this is my second problem: when add this value into entrySet? It seemed that it should be the put method achieve these thing. and below is put method.

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

it invoke putVal method and below is some code of putVal

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 invoke

 ++modCount;

the entrySet is empty and before invoke

 afterNodeInsertion(evict);

the entrySet has a element. but it seemed the code between these two line has nothing with entrySet. I think maybe there exist several threads operate entrySet then I write a small tool with jvm_ti to print the threadID which invoke the class below package java.util and find there is only one thread.

Then what I miss? Does there exist issue at process of debug? Wish I have describe my problem clearly and everything would be appreciate.

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

like image 585
nail fei Avatar asked Jan 18 '17 08:01

nail fei


People also ask

How does HashMap entrySet work?

HashMap entrySet() Method in Java util. HashMap. entrySet() method in Java is used to create a set out of the same elements contained in the hash map. It basically returns a set view of the hash map or we can create a new set and store the map elements into them.

How HashMap put a new entry and gets it?

put() method of HashMap is used to insert a mapping into a map. This means we can insert a specific key and the value it is mapping to into a particular map. If an existing key is passed then the previous value gets replaced by the new value. If a new pair is passed, then the pair gets inserted as a whole.

What is the return type of entrySet () in HashMap?

The Java HashMap entrySet() returns a set view of all the mappings (entries) present in the hashmap. Here, hashmap is an object of the HashMap class.

How do I initialize a HashMap?

The Static Initializer for a Static HashMap We can also initialize the map using the double-brace syntax: Map<String, String> doubleBraceMap = new HashMap<String, String>() {{ put("key1", "value1"); put("key2", "value2"); }};


2 Answers

It is your debugger that fools you. The debugger view calls toString() which in fact calls entrySet() (see AbstractMap.toString()). That is why the entrySet was already initialized, when you looked at it.

If you look in there via reflection utils, e.g. 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 get the following output:

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

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

The same applies to your second question. If you look at the values "reflectively":

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

you get, as expected:

entrySet = null
like image 74
Roland Avatar answered Oct 29 '22 20:10

Roland


Quick look into source code reveals that it is allocated lazily:

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

Reference

I think maybe there exist several threads operate entrySet then I write a small tool with jvm_ti to print the threadID which invoke the class below package java.util and find there is only one thread.

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

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

inside HashMap.

like image 28
rkosegi Avatar answered Oct 29 '22 21:10

rkosegi