I have a below method. The implementation is if the map does not have id
key, then the method inserts id
and name
.
But Sonar reports this line of code as a Major error and suggests to replace Map.get()
and condition with a call to Map.computeIfAbsent()
.
How to insert values into map using computeIfAbsent
?
Original Code
Here Sonar complains:
public void testMethod(List<Map<String, Object>> restultMapList) {
for(Map<String, Object> resultMap : restultMapList) {
if(resultMap.get("id")==null) {
resultMap.put("id", "");
resultMap.put("name", "");
}
}
}
Attempt to use computeIfAbsent
I have tried the below but it throws error. Not sure how to fix this.
public void testMethod(List<Map<String, Object>> restultMapList) {
for(Map<String, Object> resultMap : restultMapList) {
resultMap.computeIfAbsent("id", x-> x.put("id",""));
}
}
Then following errors are shown:
Multiple markers at this line
- Lambda expressions are allowed only at source level 1.8 or above
- The method put(String, String) is undefined for the type String
CodePudding user response:
Just return the String value from the lambda function without any operations.
Alternatively, using putIfAbsent
would make more sense as there in no computation involved. computeIfAbsent makes more sense where getting the value would involve some operation (e.g. in cache, if not present fetch from original source).
resultMap.putIfAbsent("id", "");
As for computeIfAbsent,
For reference, Method's Javadoc says
If the specified key is not already associated with a value (or is mapped to null), attempts to compute its value using the given mapping function and enters it into this map unless null.
Thus in your case, there is no computation as such - so you can return the string itself. The definition of the function is:
Params: key – key with which the specified value is to be associated
mappingFunction – the function to compute a value
Returns: the current (existing or computed) value associated with the specified key, or null if the computed value is null
The following snippet will work for you:
Map<String, String> resultMap = new HashMap<>();
resultMap.computeIfAbsent("id", s -> "FirstValue");
System.out.println(resultMap.get("id")); // FirstValue
resultMap.computeIfAbsent("id", s -> "NewValue");
System.out.println(resultMap.get("id"));
// Still FirstValue as id was already present
Here the Lamda s -> "NewValue"
can be expanded to (String s) -> "NewValue"
For the second part - the error "Lambda expressions are allowed only at source level 1.8 or above" indicates wrong environment in IDE probably, where you would need to set language API level to 1.8 or above, or check the library settings.
CodePudding user response:
The Rule
The Sonar rule for Java with RSPEC-3824 applies for Java 8 only:
"Map.get" and value test should be replaced with single method call
It’s a common pattern to test the result of a
java.util.Map.get()
againstnull
or callingjava.util.Map.containsKey()
before proceeding with adding or changing the value in the map. However thejava.util.Map
API offers a significantly better alternative in the form of thecomputeIfPresent()
andcomputeIfAbsent()
methods. Using these instead leads to cleaner and more readable code.Note that this rule is automatically disabled when the project’s sonar.java.source is not 8.
Let's assume your project is configured for the use of Java 8.
The Sonar compliant solution
From:
if (resultMap.get("id") == null) {
resultMap.put("id", "");
resultMap.put("name", "");
}
To use with putIfAbsent(key, value)
:
resultMap.putIfAbsent("id", ""); // empty string as default
Note:
- this solution does not compute anything, no lambda needed.
- here
resultMap.put("name", "");
is not called if key"id"
was not present.
Both methods putIfAbsent
and computeIfAbsent
were added to interface Map
with Java version 8.
See also:
- Tutorial on Baeldung: A Guide to Java HashMap, 4.3. putIfAbsent()
- Tutorial on Baeldung: The Map.computeIfAbsent() Method