2017-01-18 28 views
5

znajdę bardzo magiczne rzeczy, prosty kod jak poniżej:jak i kiedy HashMap zainicjować entrySet i dodać wartość do niego

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"); 
    } 
} 

po powołać

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

the state of map object pole zmienna entrySet nie jest null, to znaczy zostało zainicjowane.


Wtedy to mój pierwszypytanie, kiedy entrySet została zainicjowana? wydawało odpowiedni kod powinien znajdować się w konstrukcie HashMap, ale poniżej jest kod źródłowy tego konstruktora

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

wydawało tam nie istnieje kod, który zainicjować entrySet.

i coś się dzieje. po powołać

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

zawartość pola zmienna stół i entrySet jak poniżej ujęcia. enter image description here To jest mój drugi problem: kiedy dodać tę wartość do zestawu wpisów? Wyglądało na to, że powinno to być put. i poniżej jest metoda put.

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

inicjować putVal sposób i poniżej pewne kod 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; 
}  

po wywołać

++modCount; 

entrySet pusty i przed wywołać

afterNodeInsertion(evict); 

element entrySet ma element. ale wydawało się, że kod pomiędzy tymi dwoma wierszami nie ma nic wspólnego z entrySet. Myślę, że istnieje kilka wątków działać entrySet następnie napisać małe narzędzie o jvm_ti wydrukować ThreadID które powołują klasę poniżej pakietu java.util i uważają, że jest tylko jeden wątek.

W takim razie tęsknię? Czy istnieje problem podczas debugowania? Chciałbym jasno opisać mój problem i wszystko byłoby docenione.

add: moja wersja Java jest 1.8.0_77 i zaćmienie wersja jest 4.6.1 i 4.5.1

+1

Dlaczego to ma znaczenie? Jak wpływa na umowę? –

+2

@SkinnyJ to nie ma znaczenia, ale chcę znać faktyczne szczegóły i napotkać pewne pytanie. –

+0

Czy można dodać punkt przerwania do metody entrySet() w HashMap? Zakładam, że tam został zainicjowany zestaw wpisów. Jeśli umieścisz tam punkt przerwania i wątek się zatrzyma, zobaczysz ślad stosu kodu, który wywołuje tę metodę. – toongeorges

Odpowiedz

6

To twój debugger, który cię głupia. Widok debuggera wywołuje toString(), co w rzeczywistości wywołuje entrySet() (patrz AbstractMap.toString()). Dlatego entrySet został już zainicjalizowany, kiedy na niego spojrzałeś.

Jeśli zaglądasz tam za pomocą narzędzi do refleksji, np. z następującego kodu:

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); 

uzyskać następujący wynik:

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

Jak widać: entrySet w rzeczywistości jest jeszcze null jeśli nie toString() nazywa i zostanie zainicjowana po niej.

To samo dotyczy drugiego pytania. Jeśli spojrzeć na wartości "refleksyjnie":

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

masz, zgodnie z oczekiwaniami:

entrySet = null 
+0

To jest dokładnie to, co dzieje się podczas debugowania, dobry punkt z AbstractMap! – rkosegi

+0

@Roland cudowny, ale nadal istnieje dodatkowy problem, jeśli użyję 'map.toString()' w drugim przypadku, wypisze * [key = value] * zamiast * [] *. In * entrySet * method to po prostu nowy obiekt * EntrySet * za pośrednictwem domyślnego konstruktora. Nie wiem, kiedy element został wstawiony do * entrySet *. –

+0

Masz na myśli, w jaki sposób entrySet wie o wartościach? Jeśli tak, to: 'EntrySet' jest klasą wewnętrzną, więc wie wszystko ze swojego kontenera. Prawdopodobnie jest ukryty gdzieś w pobliżu węzłów ... Nie chcę tam zbyt głęboko zanurzać. ;-) – Roland

1

Szybkie spojrzenie na kod źródłowy ujawnia, że ​​jest alokowana leniwie:

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

Reference

Myślę, że być może istnieje kilka wątków obsługujących entrySet, następnie I napisać małe narzędzie z jvm_ti, aby wydrukować threadID, który wywołuje klasę poniżej pakietu java.util i znaleźć tylko jeden wątek.

Nie, zdecydowanie nie są zaangażowane NO wątków (chyba, że ​​wyraźnie je stworzył) Jeśli chcesz debugować go łatwo ustawić watchpoint na

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

wewnątrz HashMap.

+0

Jesteś blisko :) – xenteros

+0

@rkosegi Zgadzam się, że ta metoda zainicjalizuje entrySet, **, ale ** znajduję przed wykonaniem mojej pierwszej instrukcji 'HashMap map = new HashMap ();' ta metoda została wywołana i nie powinno to mieć nic z moim * obiektem mapy *. Następnie ta metoda nie została wywołana. –

+0

@nailfei Jestem prawie pewien, że to była kolejna HashMap. –