Removing map entries by value (rather than by key) requires a slightly different approach than map.remove(key). Java provides clean, expressive ways to do this using the map's values collection.
The Setup
All examples below use this map:
import java.util.*;
Map<Integer, String> inventory = new HashMap<>();
inventory.put(1, "apple");
inventory.put(2, "banana");
inventory.put(3, "cherry");
inventory.put(4, "banana");
inventory.put(5, "date");
Goal: remove all entries where the value is "banana" (keys 2 and 4).
Method 1: removeIf() on values()
The cleanest, most expressive approach:
inventory.values().removeIf("banana"::equals);
System.out.println(inventory);
// {1=apple, 3=cherry, 5=date}
map.values() returns a live view of the map's values. Modifying it (including removing) directly modifies the underlying map. removeIf() accepts a predicate and removes all matching entries.
With a lambda:
inventory.values().removeIf(value -> value.equals("banana"));
Method 2: removeAll() on values()
Removes all entries matching any value in a given collection:
inventory.values().removeAll(Collections.singleton("banana"));
Collections.singleton("banana") creates a single-element set containing "banana". This is equivalent to removeIf("banana"::equals) but useful when you want to remove multiple distinct values at once:
// Remove all entries with value "banana" OR "cherry"
inventory.values().removeAll(Set.of("banana", "cherry"));
Method 3: entrySet() with removeIf()
Use this when your removal condition depends on both the key and the value:
// Remove all entries where the key is even AND the value starts with "b"
inventory.entrySet().removeIf(entry ->
entry.getKey() % 2 == 0 && entry.getValue().startsWith("b"));
This is the most flexible approach for complex conditions.
Method 4: Iterator (Pre-Java 8)
For environments that can't use lambdas:
Iterator<Map.Entry<Integer, String>> it = inventory.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, String> entry = it.next();
if ("banana".equals(entry.getValue())) {
it.remove(); // safe removal during iteration
}
}
Do not usemap.remove()inside a for-each loop — it will throwConcurrentModificationException. The iterator'sremove()method is the safe way to delete during iteration.
Comparison
| Method | Readable? | Complex conditions? | Java version |
|---|---|---|---|
values().removeIf() | Excellent | Value only | Java 8+ |
values().removeAll() | Good | Multiple values | Java 8+ |
entrySet().removeIf() | Good | Key + value | Java 8+ |
| Iterator | Verbose | Key + value | All |
Removing Null Values
To remove all entries where the value is null, the same removeIf() approach works — but use Objects.isNull() instead of .equals() to avoid a NullPointerException:
import java.util.Objects;
Map<String, String> config = new HashMap<>();
config.put("host", "localhost");
config.put("port", null);
config.put("user", "admin");
config.put("pass", null);
config.values().removeIf(Objects::isNull);
System.out.println(config); // {host=localhost, user=admin}
Calling "banana"::equals on a null value would throw a NullPointerException, so always use Objects::isNull or a null-safe predicate when nulls are possible.
Producing a New Filtered Map (Without Mutating the Original)
All the methods above modify the map in-place. If you want to keep the original and produce a new filtered map, use a stream:
Map<Integer, String> filtered = inventory.entrySet()
.stream()
.filter(entry -> !entry.getValue().equals("banana"))
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue
));
System.out.println(inventory); // original unchanged
System.out.println(filtered); // {1=apple, 3=cherry, 5=date}
This is the right approach when the original map must remain intact — for example, when it's a read-only view shared across multiple parts of your application.
Pitfall: Unmodifiable Maps
If your map was created with Map.of(), Map.copyOf(), or Collections.unmodifiableMap(), all the removal methods above will throw UnsupportedOperationException:
// This map is unmodifiable — removal will fail
Map<Integer, String> immutable = Map.of(1, "apple", 2, "banana");
immutable.values().removeIf("banana"::equals); // UnsupportedOperationException!
// Make a mutable copy first
Map<Integer, String> mutable = new HashMap<>(immutable);
mutable.values().removeIf("banana"::equals);
If you receive a map from an external source and don't know whether it's mutable, wrap it in new HashMap<>(map) before attempting any removal.
Summary
For most cases, values().removeIf(value -> ...) is the cleanest option. When you need to check both key and value, use entrySet().removeIf(entry -> ...). Use Objects::isNull when removing null values. If you need to preserve the original, produce a new filtered map with a stream instead of mutating. Avoid modifying the map inside a for-each loop — always use removeIf() or an explicit iterator.
No comments :
Post a Comment
Please leave your message queries or suggetions.
Note: Only a member of this blog may post a comment.