I am using an object of type
Map<Long, Map<UUID, OperationEntry>> stepOperations = new ConcurrentHashMap<>();
to control the execution of some operations. I have a Kafka message handler method that, upon receiving a message about a status change of an operation, needs to find and update the state of a record in stepOperations map based on the passed UUID.
UUID class is not overriden (standatd java.util.UUID).
What could be the reason for getting an unexpected result from the containsKey method if the key "3102869c-7653-4fb7-ad47-a629b5cbac90", as clearly seen in the screenshot, is present in the map?
Here is the problematic part of the code:
private OperationEntry getOperationLogEntry(UUID requestId) {
return stepOperations.values().stream()
.filter(value -> value.containsKey(requestId))
.map(value -> value.get(requestId))
.findFirst()
.orElse(null);
}
The ConcurrentHashMap class is used due to its guaranted support for multithreaded processing. The incoming Kafka messages are processed by a thread pool consisting of 16 threads.
Could the presence of a inner map be a potential issue here?
Simple test such as
Map<UUID, String> testMap = new java.util.concurrent.ConcurrentHashMap<>();
UUID key = UUID.randomUUID();
testMap.put(key, "testValue");
if (testMap.containsKey(key)) {
System.out.println(testMap.get(key));
}
works as it's expected to be
In the debugger, we can see that value
variable in an HashMap
. Not a ConcurrentHashMap
:
When you work with multi levels of map, you should use computeIfAbsent
.
From your code, you should probably:
stepOperations
be ConcurrentMap<Long, ConcurrentMap<UUID, Whatever>>
to make sure the two levels are good.computeIfAbsent
as in stepOperations.computeIfAbsent(key, ignored -> new ConcurrentHashMap<>())
To add a new step:
stepOperations.computeIfAbsent(opId, ignored -> new ConcurrentHashMap<>()))
.put(opUUID, opWhatever)
Finally, you could probably rewrite your code:
stepOperations
.values()
.stream()
.filter(value -> value.containsKey(operationRunState.requestId))
.map(value -> value.get(operationRunState.requestId))
.findFirst()
Do that instead:
var id = operationRunState.requestId;
stepOperations
.values()
.stream().map(value -> value.get(id))
.filter(Objects::nonNull)
.findFirst();
Two points:
id
to ensure you use the same idConcurrentHashMap
forbids key and value from being null
: the containsKey
is useless here.