remove()

Delete a mapping by key or by key and value, and remove entries safely while iterating without a ConcurrentModificationException.

Signature
V remove(Object key)
  • Added in Java Java 1.2

Parameters

ParameterTypeDescription
$keyObjectThe key whose mapping should be removed.
$valueoptionalObjectUsed by the overload remove(Object key, Object value): the entry is removed only if the key is currently mapped to this value.

Return value

VThe value that was mapped to the key, or null if there was no mapping. The overload remove(key, value) returns true if an entry was removed.

Errors

UnsupportedOperationException if the map is unmodifiable; NullPointerException if the key is null and the map does not permit null keys; ClassCastException if the key's type is not compatible (optional).

Changelog

  • PHP Java 8Added the default method remove(Object key, Object value), which removes the entry only if the key is currently mapped to the given value.

remove(key) deletes the mapping for a key and returns the value that was there, or null if the key was absent. Removing a missing key is not an error, which makes it safe to call twice: a logout handler can remove the session without checking first.

Conditional remove

Java 8 added remove(key, value). It removes the entry only if the key is currently mapped to that value, and returns true if it did. On a ConcurrentHashMap this check and the delete happen atomically, so it works as a compare-and-delete: a worker releases a lock entry only if it still owns it, and a stale request cannot delete a session that has since been replaced.

Removing while iterating

Calling map.remove inside a for-each loop over the same map is the classic mistake. HashMap, LinkedHashMap and TreeMap notice the change and throw ConcurrentModificationException on the next step of the loop. The name is misleading: no second thread is needed.

Remove through the views instead:

cache.entrySet().removeIf(e -> e.getValue().isExpired());
sessions.values().removeIf(s -> s.userId().equals(userId));
map.keySet().removeAll(keysToDrop);

Or use an explicit Iterator and call iterator.remove(), which is the one structural change the iterator allows. ConcurrentHashMap iterators never throw ConcurrentModificationException, so removing during iteration there is allowed.

Removing by value

There is no removeByValue. map.values().remove(v) removes one entry with that value; map.values().removeIf(v::equals) removes all of them. Both scan the whole map.

Other notes

On a map from Map.of or Map.copyOf, remove throws UnsupportedOperationException. Removing an entry does not shrink a HashMap's internal table, so a huge map that is emptied keeps its memory until it is replaced. For caches that grow and shrink, consider a size-bounded cache rather than manual removal.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Invalidating a session token on logout, safely even if the handler runs twice.
  • Evicting expired entries from an in-memory cache on a scheduled clean-up task.
  • Releasing an in-process lock on a resource only when the current worker still holds it.

Frequently asked questions

How do I remove entries from a map while iterating?

Use map.entrySet().removeIf(predicate), or loop with an explicit Iterator and call iterator.remove(). Calling map.remove inside a for-each loop throws ConcurrentModificationException on HashMap, LinkedHashMap and TreeMap.

What does Map.remove return if the key is not found?

null, and the map is unchanged. No exception is thrown, except on unmodifiable maps, which always throw UnsupportedOperationException.

How do I remove a map entry by its value?

map.values().remove(value) removes one matching entry; map.values().removeIf(value::equals) removes every entry with that value.

More examples

Removing expired cache entries with removeIf

removeIf on the entry set deletes from the map itself, in one pass and without a ConcurrentModificationException. It returns true if anything was removed.

Example · java
record CacheEntry(String value, long expiresAt) {}

void main() {
    long now = 1_000; // a fixed clock value for the demo
    Map<String, CacheEntry> cache = new LinkedHashMap<>();
    cache.put("a", new CacheEntry("A", 900));
    cache.put("b", new CacheEntry("B", 1_500));
    cache.put("c", new CacheEntry("C", 1_000));

    boolean changed = cache.entrySet().removeIf(e -> e.getValue().expiresAt() <= now);
    IO.println("removed any: " + changed);
    IO.println(cache.keySet());
}
// Output:
// removed any: true
// [b]

Removing inside a for-each loop throws

The loop removed svc-a and then failed on its next step, leaving the job half done. The iterator's own remove method is allowed and finishes the clean-up.

Example · java
void main() {
    Map<String, Integer> failures = new LinkedHashMap<>();
    failures.put("svc-a", 0);
    failures.put("svc-b", 3);
    failures.put("svc-c", 0);

    try {
        for (String key : failures.keySet()) {
            if (failures.get(key) == 0) {
                failures.remove(key);
            }
        }
    } catch (ConcurrentModificationException e) {
        IO.println("ConcurrentModificationException");
    }
    IO.println(failures);

    Iterator<Map.Entry<String, Integer>> it = failures.entrySet().iterator();
    while (it.hasNext()) {
        if (it.next().getValue() == 0) {
            it.remove();
        }
    }
    IO.println(failures);
}
// Output:
// ConcurrentModificationException
// {svc-b=3, svc-c=0}
// {svc-b=3}

Releasing a lock only if you still own it

The two-argument remove compares the current value before deleting, atomically on ConcurrentHashMap. A worker that lost the lock cannot release someone else's.

Example · java
void main() {
    Map<String, String> locks = new ConcurrentHashMap<>();
    locks.put("invoice-42", "worker-1");

    boolean released = locks.remove("invoice-42", "worker-2");
    IO.println("worker-2 released: " + released);

    released = locks.remove("invoice-42", "worker-1");
    IO.println("worker-1 released: " + released);
    IO.println("locks left: " + locks.size());
}
// Output:
// worker-2 released: false
// worker-1 released: true
// locks left: 0

Discussion

  • Be the first to comment on this lesson.