Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why the iterator on map.vaules can be used to remove HashMap#Entry?

I'm trying to remove all the entry of which the value is null. The code is:

Map<String, String> map = new HashMap<>();
map.put("one", null);
map.put("two", null);
map.put("three", "THREE");

Iterator iterator = map.values().iterator();
while (iterator.hasNext())
{
    if (iterator.next() == null) {
        iterator.remove();
    }
}

for (Map.Entry<String, String> e : map.entrySet()) {
    System.out.println(e.getKey() + ":" + e.getValue());
}

My question is iterator is bind to map.values, why it can remove the whole entry?

like image 312
caisil Avatar asked Jul 24 '17 22:07

caisil


People also ask

Can we remove element from HashMap while iterating?

While iterating, check for the key at that iteration to be equal to the key specified. The entry key of the Map can be obtained with the help of entry. getKey() method. If the key matches, remove the entry of that iteration from the HashMap using remove() method.

Can we remove key from map while iterating?

@HDave It's possible using a method like Map. remove (Object key) , but should not be done during iteration.

How do you remove a mapping while iterating over HashMap in Java?

You should always use Iterator's remove() method to remove any mapping from the map while iterating over it to avoid any error.

How do I remove a value from a HashMap?

HashMap remove() Method in Java HashMap. remove() is an inbuilt method of HashMap class and is used to remove the mapping of any particular key from the map. It basically removes the values for any particular key in the Map. Parameters: The method takes one parameter key whose mapping is to be removed from the Map.


2 Answers

It is possible because Map#values returns a view of the values that is backed by the map.

From the official Java-Doc of Map#values:

Returns a Collection view of the values contained in this map. The collection is backed by the map, so changes to the map are reflected in the collection, and vice-versa. [...] The collection supports element removal, which removes the corresponding mapping from the map, via the Iterator.remove, Collection.remove, removeAll, retainAll and clear operations. It does not support the add or addAll operations.


Note that the AbstractMap class, from which most map implementations extend, has an extra field transient volatile Collection<V> values and that is exactly what you will get there. As you see the collection is internally used by the Map and thus changes on it are also reflected on the Map itself. See also: Source code of AbstractMap


If you like to go in detail, take a look at the AbstractMap#values method in the source code. There they create the values-collection as a wrapper that operates on the original map. For example its next method iterates on the entries Entry<K, V> of the Map but only return their value with Entry#getValue and so on.
Also the remove method, as you can see, gets passed over to the iterator of Entry<K, V>, thus the remove will in the end be executed on the original map again.

like image 83
Zabuzard Avatar answered Nov 15 '22 03:11

Zabuzard


Explanations have been given by Zabuza, but because there is proper way to remove your elements, I write them :


To remove Entry with null value you can use Streams :

map = map.entrySet()
         .stream()
         .filter(entry -> entry.getValue()!=null)
         .collect(Collectors.toMap(Map.Entry::getKey,Map.Entry::getValue));

Or in one - line : map.entrySet().removeIf(e -> e.getValue()==null);

Or : map.values().removeIf(v -> v == null)

like image 37
azro Avatar answered Nov 15 '22 03:11

azro