TreeMap iterator.remove () changes the last record

I have a map.

Map<Integer,String> map = ... 

There are n elements in the map (take these 9 for this example)

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

Now I want to iterate over this map and remove the nth element using an 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(); } } } 

From javadoc, I would suggest the removal semantics are well defined.

But depending on the implementation of the card - i.e. HashMap vs TreeMap there is a difference whether it.remove() is executed before or after entry.getValue() .

for HashMaps map = new HashMap<>() behavior

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

for TreeMap map = new TreeMap<>() behavior is the same when I delete the current record from the iterator after I access it:

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

leads to

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

so good, but if I delete the item before I access the record:

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

Exit unexpectedly

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

Apparently, it.remove() from TreeMap modifies Entries because TreeMap consists of Entries , and the iterator actually returns the actual elements of the map. And depending on the current position in the tree, the internal links are the entry points to the next or current (deleted) element.

But I'm not sure if this is a mistake, or if it is intentional. If the latter is the case, I wonder about the rationale?

Edit: Source code TreeMap iterator.remove ()

+5
source share
2 answers

From Map.Entry Javadoc :

map record behavior is undefined if the support map was changed after the record was returned by an iterator, except for the setValue operation on the map record

And from Map.Entry.getValue Javadoc :

If the mapping has been removed from the support map (using the iterator delete operation), the results of this call are undefined.

Calling entry.getValue() after it.remove() not allowed. Java does not make promises about what will happen if you try. You must get the value before deleting the entry.

+2
source

This is a partial answer, but hopefully it will tell someone else how to give a more accurate answer.

Assuming the call you make in Iterator#next() returns a view sitting on top of the base map,

Then it is not surprising that we could get some undefined behavior when trying to access the value after the base record of the map was deleted from the map.

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

According to your own observations, the behavior of the Map seems to be implementation specific, implying that the implementation of a particular map is involved in the behavior .

0
source

Source: https://habr.com/ru/post/1271199/


All Articles