2015-06-08 11 views
11

Der Quellcode von HashMap.values() alsWie geben HashMap.values ​​() und HashMap.keySet() Werte und Schlüssel zurück?

folgt gezeigt
public Collection<V> values() { 
    Collection<V> vs = values; 
    return (vs != null ? vs : (values = new Values())); 
} 

Wie Sie sehen können, wenn die values() Methode zunächst nur gibt ein Values Objekt aufgerufen, es. Das Objekt Values ist eine Unterklasse von AbstractCollection ohne Konstruktor und enthält natürlich kein Element. Aber als ich die Methode anrief, gab es schnell eine Sammlung zurück

Collection<String> values = map.values(); 
System.out.println(values); 

Das ist so komisch. Nicht nur values(), sondern auch keySet() und entrySet() Methode solche leere Objekte zurückgeben. Also, hier ist meine Frage, wann und wie geben diese Methoden Objekte mit Elementen zurück, die wir brauchen?

Antwort

20

Es ist ein Missverständnis, dass die Values Klasse "natürlich leer" ist. Nur weil es keine Methode gibt und ihr Konstruktor keine Argumente hat, heißt das nicht, dass die Sammlung leer ist.

Values Die Klasse ist eine „innere Klasse“ (ein nicht-statisches nested class) von HashMap, was bedeutet, daß es einen impliziten Bezug auf das Objekt HashMap hat, die es erstellt hat. Es kann daher auf alle Elemente der HashMap zugreifen, entweder explizit unter Verwendung der HashMap.this Referenz oder durch direkten Zugriff auf die Mitglieder. Da es sich um eine innere Klasse handelt, ist es sogar erlaubt, auf die privaten Mitglieder der HashMap zuzugreifen.

Sie können zum Beispiel in der Values Klasse sehen, dass die Umsetzung der size Methode:

public int size() { 
    return size; 
} 

Die Values Klasse keinen size Mitglied, so dass size auf die HashMap bezieht sich ‚Größe s.Es ist äquivalent zu:

public int size() { 
    return HashMap.this.size; 
} 

EDIT: Beachten Sie, dass dies bedeutet auch, dass die Sammlung, die Sie erhalten keine Kopie, sondern bezieht sich immer noch auf die ursprünglichen HashMap Inhalt und daher ändert sich, wenn Sie die HashMap aktualisieren:

// Getting the entry set (not a copy!) 
    Set<Entry<String, String>> entries = map.entrySet(); 

    // Add elements to the map afterwards 
    map.put("abc", "def"); 

    // Check out the entries in the collection 
    // (magically containing the elements added after getting the collection) 
    System.out.println(entries); // "[abc=def]" 
+1

'Beachten Sie, dass dies bedeutet, dass die empfangene Sammlung keine Kopie ist, sondern sich immer noch auf den ursprünglichen HashMap-Inhalt bezieht und sich daher ändert, wenn Sie die HashMap aktualisieren:' Das ist auch im Vertrag von 'Map.values ​​() ', um Missverständnisse zu vermeiden. – biziclop

+0

Eine innere Klasse ist eine nicht statische geschachtelte Klasse, d. H. Wäre die Klasse statisch gewesen, wäre sie keine innere Klasse, sondern einfach eine statische verschachtelte Klasse. https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html – Sardtok

+0

@Sardtok: Danke für den Hinweis, ich habe nur die Antwort bearbeitet, um die offizielle Terminologie zu verwenden. – mastov

13

Die Klasse Values implementiert eine Collection, die von der HashMap unterstützt wird. Wie von mastov kommentiert, ist Values eine innere Klasse von HashMap, die Zugriff auf die Mitglieder der umschließenden HashMap-Instanz gibt, mit der sie verknüpft ist. Deshalb ist es nicht leer. Seine Größe ist die Größe des HashMap, und wenn Sie darüber iterieren, durchlaufen Sie die Einträge des HashMap.

Wenn Sie System.out.println(values); rufen, rufen Sie die toString Methode von AbstractCollection, die eine Iterator verwendet über die Werte zu durchlaufen und ihre String Darstellung bekommen. Die Iterator iteriert tatsächlich über die Einträge der HashMap und gibt ihre Werte zurück.

Das gleiche gilt für die Set s zurückgegeben von keySet und entrySet.

+0

Vielleicht sollten Sie erwähnen, dass die 'Values'-Klasse eine nicht statische innere Klasse ist, die daher einen impliziten 'this'-Zeiger auf die äußere Klasse' HashMap' hat. So kann es auf die Elemente der Map verweisen, selbst wenn der Konstruktor ohne Argumente aufgerufen wird. – mastov

+0

@mastov guten Punkt – Eran

+0

Dank @Eran und @mastov. Ich weiß, dass die Iterator-Methode gut funktionieren kann, wenn wir Werte "iterieren". Wenn ich jedoch einen Haltepunkt an der Collection values ​​= map.values ​​(); 'Zeile gesetzt habe und das Programm debugge, wurden die Werte bereits als Sammlung festgelegt, bevor' System.out.println (Werte) 'aufgerufen wurde. und hier gibt es keine Iteration. Das ist der Punkt, an dem ich mich komisch fühle. – Yohn

2
Collection<V> vs = values; 
return (vs != null ? vs : (values = new Values())); 

Diese beiden Zeilen beantworten Ihre Frage.

values ist eine interne Sammlung (Geerbt von AbstractMap). Wenn es nicht null ist, wird es zurückgegeben. Wenn es null ist, wird es initialisiert und das neue wird zurückgegeben.

Jetzt, ich denke, der Hauptpunkt Ihrer Frage ist
wann und wie diese Methoden Objekte mit Elementen zurückgeben, die wir brauchen?

Technisch values immer dieses initialisierte Objekt zurückgeben. Wie bekommen wir dann unsere Eingabewerte von diesen Objekten?
Lässt ein wenig tief gehen:

values ​​() tatsächlich ein Objekt der Klasse zurückgeben Values, die eine innere Klasse von HashMap ist und sich AbstractCollection

private final class Values extends AbstractCollection<V> 

Wie Sie in den Quellcode ausgesehen haben. Dann werden Sie in der Values Klasse finden

public Iterator<V> iterator() { 
     return newValueIterator(); 
    } 

diese newValueIterator() funktioniert der Trick.
Wenn Sie mehr tiefer gehen, werden Sie newValueIterator() kehrt Objekt von ValueIterator finden, die eine Unterklasse von HashIterator HashIterator implementiert für Iteration grundlegende Funktionalität ist, die itterate tatsächlich über die table vom HashMap gehalten.

Verwandte Themen