2009-01-15 5 views
36

Ich versuche, den folgenden Code zu kompilieren:Wie verwende ich eine foreach-Schleife in Java, um die Werte in einer HashMap zu durchlaufen?

private String dataToString(){ 
    Map data = (HashMap<MyClass.Key, String>) getData(); 
    String toString = ""; 
    for(MyClass.Key key: data.keySet()){ 
     toString += key.toString() + ": " + data.get(key); 
    return toString; 
} 

Ich erhalte eine Fehlermeldung in dem für Zeile, die sagt:

 
incompatible types 
found : java.lang.Object 
required: MyClass.Key 

Die getData() Methode gibt ein Object (aber in diesem Fall ist die Object zurück hat die HashMap Struktur). MyClass.Key ist eine Enumeration, die ich für die Zwecke meiner Anwendung erstellt habe (in einer anderen Klassendatei - MyClass).

Wenn ich eine foreach-Schleife mit der gleichen Struktur in MyClass.java erstellt habe, trat dieses Problem nicht auf.

Was mache ich falsch?

+0

Sie müssen getData() nicht auf eine HashMap anwenden, wenn Sie sie nur einer Map zuweisen. Ziehe es lieber auf eine Karte. Was, wenn getData() eine non-HashMap (wie eine TreeMap) zurückgibt? –

+0

Ich habe hier einige Informationen weggelassen ... getData() ist eigentlich getData (String key), wobei key das gewünschte Objekt angibt, das ich bekommen möchte. Da ich das Objekt, das ich bekomme, kenne, weiß ich genau, worauf ich es anwenden sollte. – troyal

Antwort

42

Eine etwas effizienter Weg, dies zu tun:

Map<MyClass.Key, String> data = (HashMap<MyClass.Key, String>) getData(); 
    StringBuffer sb = new StringBuffer(); 
    for (Map.Entry<MyClass.Key,String> entry : data.entrySet()) { 
     sb.append(entry.getKey()); 
     sb.append(": "); 
     sb.append(entry.getValue()); 
    } 
    return sb.toString(); 

Wenn möglich, definieren „getData“ also brauchst du die Besetzung nicht.

+0

Upvoted dies seit .EntrySet() ist die effizienteste Art der Iteration durch Map und es enthält ursprüngliche Verweise auf die Karte, also, wenn Sie den Eintrag ändern Sie die tatsächliche Karte ändern und so weiter. Es ist auch sehr praktisch. – Esko

+0

Ich würde, aber getData() wird überall in diesem Projekt sowie anderen verwendet, so ist es am besten, es so zu lassen (ist ein Objekt zurückgeben). – troyal

+0

+1, sollten Sie unbedingt getData() ändern, um Map zurückzugeben. Es wird älteren Code nicht brechen. –

38

Wechsel:

Map data = (HashMap<MyClass.Key, String>) getData(); 

zu

Map<MyClass.Key, String> data = (HashMap<MyClass.Key, String>) getData(); 

Das Problem ist, dass data.keySet() eine Collection<Object> zurück, wenn Daten nur Map ist. Sobald Sie es generisch machen, gibt keySet() eine Collection<MyClass.Key> zurück. Noch besser ... iterieren über die entrySet(), die eine Collection<MyClass.Key, String> sein wird. Es vermeidet die zusätzlichen Hash-Lookups.

+0

Danke! Kannst du erklären, warum das das repariert? – troyal

+0

Da die Art, wie Sie es deklariert haben, Kartendaten, deklariert Daten als eine Karte des unbekannten Typs, so gibt keySet() Object zurück. Die Änderung teilt dem Compiler mit, dass die Schlüssel MyClass.Key und nicht Object sind. –

+0

Wenn Sie getData nur Map-Daten zuweisen, ordnen Sie diese Map zu, indem Sie sie explizit als Map definieren -Daten, damit die Foreach-Schleife Wissen über den Typ des Schlüssels behalten kann . –

3

Motlins Antwort ist korrekt.

Ich habe zwei Noten ...

  1. nicht toString += ..., verwenden Sie aber StringBuilder stattdessen verwenden und Daten anhängen.

  2. Besetzung, die Martin vorgeschlagen hat, wird Ihnen unkontrollierte Warnung geben, die Sie nicht loswerden können, weil es wirklich unsicher ist.

Ein anderer Weg, ohne (und mit String) Warnung:

private String dataToString(){ 
    Map<?, ?> data = (Map<?, ?>) getData(); 
    StringBuilder toString = new StringBuilder(); 
    for (Object key: data.keySet()) { 
     toString.append(key.toString()); 
     toString.append(": "); 
     toString.append(data.get(key)); 
    } 
    return toString.toString(); 
} 

Dies funktioniert, weil toString Methode, die Sie auf key rufen in Objektklasse definiert ist, so dass Sie bei Gießen nicht brauchen alle.

Die Verwendung von entrySet ist noch besser, da es keine weitere Suche in der Karte durchführen muss.

+0

Sie können die Warnung mit @SuppressWarning ("unchecked") loswerden –

+0

Das ist nur versteckt Probleme, nicht wirklich zu beheben. @SuppressWarning ("unchecked") sollte mit GROSSER Vorsicht verwendet werden. –

+0

Ich frage mich, ob es besser ist, data.keSet() in einer lokalen Variablen zu speichern, oder ist die foreach-Schleife optimiert? – Alfred

4

Sie konnten die entrySet greifen stattdessen zu vermeiden, dass die Schlüsselklasse benötigen:

private String dataToString(){  
    Map data = (HashMap<MyClass.Key, String>) getData();  
    String toString = "";  
    for(Map.Entry entry: data.entrySet()) {   
     toString += entry.getKey() + ": " + entry.getValue(); 
    }  
    return toString; 
} 
+0

Iterieren durch das entrySet ist effizienter als durch das keySet gehen, obwohl ich auch die Namen der Schlüssel ausgeben muss? – troyal

+0

Das entrySet ist effizienter, da Sie nicht bei jedem Schlüssel nachschlagen müssen. –

+0

@Blue - Weg effizienter, WEIL Sie Schlüssel und Wert verwenden. –

5

Ich fand dieses einfache Beispiel unter java forum.Seine Syntax ist der foreach der Liste sehr ähnlich, die, was ich suchte.

import java.util.Map.Entry; 
HashMap nameAndAges = new HashMap<String, Integer>(); 
for (Entry<String, Integer> entry : nameAndAges.entrySet()) { 
     System.out.println("Name : " + entry.getKey() + " age " + entry.getValue()); 
} 

[EDIT:] Ich habe es getestet und es funktioniert perfekt.

Verwandte Themen