2017-08-24 5 views
5

Ich habe eine Karte.TreeMap iterator.remove() ändert den letzten Eintrag

Map<Integer,String> map = ... 

Die Karte hat n Elemente (lets für dieses Beispiel dieser 9 nehmen)

map.put(1,"one"); 
    map.put(2,"two"); 
    map.put(3,"three"); 
    map.put(4,"four"); 
    map.put(5,"five"); 
    map.put(6,"six"); 
    map.put(7,"seven"); 
    map.put(8,"eigth"); 
    map.put(9,"nine"); 

Jetzt habe ich über diese Karte iterieren möge, und entfernen Sie das n-te Element des Iterator.

private void remove(int num, final Map<Integer, String> map) { 

    Iterator<Map.Entry<Integer,String>> it = map.entrySet().iterator(); 
    Map.Entry<Integer,String> entry; 
    while(it.hasNext()){ 

    entry = it.next(); 

    if(Integer.valueOf(num).equals(entry.getKey())){ 
     it.remove(); 
     System.out.println(entry.getValue()); 
     // vs 
     // System.out.println(entry.getValue()); 
     // it.remove(); 
    } 
    } 
} 

Aus dem Javadoc, ich nehme an, die Semantik des Entfernens sind gut definiert.

Aber je nach Implementierung der Karte - das heißt HashMap vs TreeMap gibt es einen Unterschied, ob die it.remove()vor oder nachentry.getValue() erfolgt.

für HashMaps map = new HashMap<>() das Verhalten

... 
remove(4, map); //output: four 
//or 
remove(5, map); //output: five 

für TreeMap map = new TreeMap<>() das Verhalten ist das gleiche, wenn ich den aktuellen Eintrag aus dem nach Iterator entfernen habe ich es erreicht:

System.out.println(entry.getValue()); 
it.remove(); 

Ergebnisse in

remove(4, map); //output: four 
//or 
remove(5, map); //output: five 

so weit, so gut, aber wenn ich das Element vor entfernen greife ich auf den Eintrag:

it.remove(); 
System.out.println(entry.getValue()); 

Der Ausgang

remove(4, map); //output: five !!! 
//or 
remove(5, map); //output: five ok 

offenbar unerwartet

ist, die it.remove() der TreeMap ändert die Entries, weil die TreeMap besteht aus Entries und der Iterator tatsächlich die tatsächlichen Elemente der Karte zurückgibt. Und abhängig von der aktuellen Position im Baum, zeigen die internen Referenzen des Entries auf das nächste oder das aktuelle (entfernte) Element.

Aber ich bin mir nicht sicher, ob das ein Fehler ist oder ob dies beabsichtigt ist. Wenn Letzteres der Fall ist, frage ich mich nach dem Grund dafür?

Edit: Quellcode der TreeMap iterator.remove()

+0

Ich bekomme nicht das Verhalten, das Sie beschreiben in jeder Karte ... welche Java-Version verwenden Sie? Was passiert, wenn Sie den Wert des Eintrags sowohl vor als auch nach dem Entfernen ausgeben()? – daniu

+0

jdk1.8.0_121 ... ist es mit einer TreeMap mit nur 3 Einträgen (1,2,3) reproduzierbar, das mittlere Element (2) entfernend. Aber es erscheint nicht in HashMap –

+0

Drucken es vor und nach den Ergebnissen in "zwei, drei" –

Antwort

2

Vom Map.Entry Javadoc:

das Verhalten eines Map-Eintrags ist nicht definiert, wenn die BackingMap modifiziert wurde, nachdem der Eintrag durch den Iterator, außer durch den setValue Betrieb auf der Karte Eintrag

zurückgegeben wurde

Und von der Map.Entry.getValue Javadoc:

Wenn das Mapping von der Backing-Map entfernt wurde (durch den Entfernungsvorgang des Iterators), sind die Ergebnisse dieses Aufrufs nicht definiert.

entry.getValue() nach it.remove() Aufruf ist verboten. Java verspricht nichts, was passieren wird, wenn Sie es versuchen. Sie sollten den Wert vor dem Entfernen des Eintrags abrufen.

0

Dies ist eine unvollständige Antwort, aber hoffentlich wird jemand anderes weg Tipp, wie eine genauere Antwort zu geben.

Unter der Annahme, der Anruf, den Sie Iterator#next() machen auf einen Blick gibt die zugrunde liegende Karte sitzen,

Dann kann es nicht überraschen, dass wir etwas undefiniertes Verhalten bekommen könnten, wenn der Wert nach zuzugreifen versuchen, Der zugrunde liegende Karteneintrag wurde aus der Karte entfernt.

while (it.hasNext()) { 
    entry = it.next(); 

    if (Integer.valueOf(num).equals(entry.getKey())) { 
     it.remove(); 
     // the object which 'entry' points to is already removed 
     // what is this pointing to? 
     System.out.println(entry.getValue()); 
    } 
} 

Durch Ihre eigenen Beobachtungen scheint das Verhalten Map Implementierung spezifisch, was bedeutet, dass die Umsetzung der jeweiligen Karte beteiligt ist in die Verhalten zu beobachten ist.

+0

Ich kann diese Antwort entfernen, wenn Sie glauben, dass es hier keinen Wert hinzufügt. Übrigens, wenn Sie Quellcode für eine Implementierung von 'Iterator' finden können, haben Sie vielleicht eine Antwort. –

+0

Ja, ich weiß, warum es passiert, aber nicht, warum es auf diese Weise implementiert wird und ob der Effekt beabsichtigt ist (IMO verletzt es den Schnittstellenvertrag). –

+0

Wo sagt der Vertrag, dass nach dem Entfernen eines Eintrags der Eintrag noch gültig sein muss? Wenn Sie den Quellcode für einen Map-Iterator nicht sehen, wie können Sie dann wissen, warum das passiert? –

Verwandte Themen