Java HashMap replace: Update Existing Entries
java hashmap replace: Learn how the Java HashMap replace method works, its two overloads, return values, and when to use it instead of put.
The java hashmap replace method provides a way to update an existing key's value without accidentally inserting a new entry. It's a common source of confusion because it looks similar to put but behaves differently. This article explains both overloads, their return values, and when to use them.
The Two replace Overloads and Their Signatures
The HashMap class inherits two replace methods from the Map interface:
V replace(K key, V value) boolean replace(K key, V oldValue, V newValue)
The first overload replaces the value for the specified key only if the key is currently mapped to some value. It returns the previous value, or null if there was no mapping. The second overload replaces the value only if the key is currently mapped to the specified oldValue. It returns true if the replacement was performed, and false otherwise.
Both methods are default methods in the Map interface, and HashMap inherits their implementations. They were added in Java 8.
How replace Differs from put
The most important distinction is that put always inserts or overwrites, while replace only updates an existing mapping. Consider this example:
Map<String, Integer> map = new HashMap<>(); map.put("count", 1); map.replace("count", 2); // updates existing key map.replace("missing", 10); // does nothing, returns null map.put("missing", 10); // inserts new entry
If you call replace on a key that does not exist, the map remains unchanged. This is useful when you want to update a value only if the key is already present, without risking an accidental insertion.
Return Values and Null Handling
The single-argument replace returns the previous value associated with the key, or null if there was no mapping. This can be ambiguous when the previous value itself is null. For example:
map.put("key", null); map.replace("key", "value"); // returns null, but key existed
The return value does not tell you whether the key existed; it only tells you the previous value. To check existence, use containsKey before calling replace, or use the two-argument overload if you need a boolean result.
The two-argument overload returns a boolean, which avoids the ambiguity. It also allows you to perform a conditional update only when the current value matches an expected value.
Practical Use Cases for replace
A common use case is updating a cache or a configuration map where you want to change a value only if it already exists. For example, updating a session attribute:
Map<String, Session> sessions = new HashMap<>(); sessions.put("user-123", new Session()); // Only update if the session is still active boolean updated = sessions.replace("user-123", oldSession, newSession);
Another use case is implementing a counter that should not create a new entry if the key is missing. You might use replace in combination with get or compute for more complex logic.
Concurrency and Atomicity Considerations
HashMap is not thread-safe. The replace methods are not atomic. If multiple threads modify the same map concurrently, you need external synchronization or a concurrent map implementation. ConcurrentHashMap provides atomic replace methods that are safe for concurrent use. For example:
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>(); map.put("count", 1); // Atomic conditional update map.replace("count", 1, 2);
If you are using a plain HashMap in a multi-threaded context, you must synchronize access yourself. The replace method does not provide any locking or atomicity guarantees.
Performance and Internal Behavior
The replace methods have the same time complexity as get and put — O(1) on average, assuming a good hash function and no excessive collisions. They do not trigger a resize unless the map is already at capacity and an insertion occurs. However, replace does not insert, so it will never cause a resize on its own.
Internally, replace locates the bucket for the key, then checks whether the key exists. If it does, it updates the value. The two-argument overload also checks the current value equality before updating. This adds a minor overhead, but it is negligible in most applications.
Common Mistakes and Edge Cases
One common mistake is using replace when you actually need to insert a new entry if the key is absent. In that case, use put or putIfAbsent. Another mistake is assuming that replace returns the new value; it returns the old value or a boolean, depending on the overload.
Be careful with null values. If your map can contain null values, the return value of the single-argument replace becomes unreliable for checking existence. Prefer the two-argument overload or use containsKey explicitly.
Also note that replace does not support custom equality for keys; it uses equals and hashCode as defined by the key class. If your key objects have mutable state that affects equality, you may encounter unexpected behavior.