2017-08-24 91 views
5

Mam mapę.TreeMap iterator.remove() modyfikuje ostatni wpis

Map<Integer,String> map = ... 

Mapa ma n elementów (Weźmy na ten przykład te 9)

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

teraz chcę iteracyjne nad tej mapie, a następnie wyjmij n-ty element używając iteratora.

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

Z javadoc, zakładam, semantyka usuwania są dobrze zdefiniowane.

Ale w zależności od wykonania mapie - tj HashMap vs TreeMap istnieje różnica czy it.remove() odbywa przed lub poentry.getValue().

dla HashMaps map = new HashMap<>() zachowanie jest

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

dla TreeMap map = new TreeMap<>() zachowanie jest taka sama, kiedy usunąć bieżący wpis z iteracyjnej po Mam obejrzano go:

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

wyników w

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

tej pory tak dobrze, ale jeśli usunąć element przed uzyskać dostęp do wpisu:

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

wyjście jest nieoczekiwanie

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

Najwyraźniej it.remove() z TreeMap modyfikuje Entries, ponieważ TreeMap składa się z Entries, a iterator faktycznie zwraca rzeczywiste elementy mapy. A w zależności od aktualnej pozycji w drzewie wewnętrzne odwołania punktu wejścia do następnego lub bieżącego (usuniętego) elementu.

Ale nie jestem pewien, czy to błąd, czy jest to celowe. Jeśli tak jest, zastanawiam się nad uzasadnieniem?

Edit: Kod źródłowy TreeMap iterator.remove()

+0

Nie widzę zachowania, które opisujesz na którejkolwiek mapie ... jakiej wersji Java używasz? Co się stanie, jeśli wypiszesz wartość pozycji przed i po usunięciu()? – daniu

+0

jdk1.8.0_121 ... jest odtwarzalny z mapą drzewa z tylko trzema wpisami (1,2,3), usuwając środkowy element (2). Ale nie pojawia się w HashMap –

+0

wydrukowanie go przed i po wynikach w "dwóch, trzech" –

Odpowiedz

2

Z Map.Entry Javadoc:

zachowanie wpisu mapy jest niezdefiniowana jeśli mapa podkładowa została zmodyfikowana po wpis został zwrócony przez iteracyjnej, z wyjątkiem poprzez działanie setValue na wejściu mapy

I od Map.Entry.getValue Javadoc:

Jeśli mapowanie zostało usunięte z mapy podkładu (przez operację usuwania iteratora), wyniki tego połączenia są niezdefiniowane.

Wywołanie entry.getValue() po it.remove() jest zabronione. Java nie obiecuje, co się stanie, jeśli spróbujesz. Powinieneś pobrać wartość przed usunięciem wpisu.

0

To jest częściowa odpowiedź, ale mam nadzieję, że przewróci się na kogoś innego, jak dać bardziej dokładną odpowiedź.

Zakładając, wywołanie dokonać do Iterator#next() zwraca widok siedzącego na górze podstawowej mapie

to nie może być zaskoczeniem, że możemy dostać jakieś niezdefiniowane zachowanie podczas próby uzyskania dostępu do wartości po podstawowy wpis mapy został usunięty z mapy.

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

według własnych obserwacji, zachowanie wydaje się być Map realizacja specyficzne, co oznacza, że ​​realizacja konkretnego mapie jest zaangażowany w które obserwuje zachowanie.

+0

Mogę usunąć tę odpowiedź, jeśli uważasz, że nie dodaje ona tutaj żadnej wartości. Przy okazji, jeśli znajdziesz kod źródłowy do implementacji 'Iteratora', możesz po prostu znaleźć odpowiedź. –

+0

Tak, wiem, dlaczego tak się dzieje, ale nie dlaczego jest to realizowane w ten sposób i jeśli efekt jest zamierzony (IMO narusza umowę z interfejsem). –

+0

Gdzie umowa mówi, że po usunięciu wpisu, wpis nadal musi być ważny? Ponadto, nie widząc kodu źródłowego dla iteratora map, jak możesz wiedzieć, dlaczego tak się dzieje? –