问题
I'm having a validate method that return a boolean.
I'm using invoking this method (Java 7) as follow:
boolean isValid = true;
for (String key: aMap.keySet()) {
isValid &= validate(key, aMap.get(key));
}
I would like to rewrite this code in Java 8.
Java 8 allows iterating across a Map using:
aMap.forEach((k,v) -> validate(k, v));
But this won't work:
aMap.forEach((k,v) -> isValid &= validate(k, v));
Question
How can I rewrite the the Java 7 code into Java 8 to achieve the same result?
Note:
I asked a similar question here . The difference in this post is that I want to iterate this time across all the items of the Map (for the validate method to build a validation report). isValid must return true if no validation error occurred, or false if at least one occurred.
回答1:
You can use
boolean isValid = aMap.entrySet().stream()
.map(e -> validate(e.getKey(), e.getValue()))
.reduce(true, Boolean::logicalAnd);
as, unlike anyMatch, allMatch, etc, Reduction knows no short-circuiting. However, your requirement of executing all validate method calls suggests that there are side-effects within that method. It’s important to understand that these side effects must be non-interfering, i.e. it should not matter in which order these method calls are made and even concurrent evaluation of multiple elements should not break it.
Even when these requirements are met, it is rather discouraged to have such side-effects in a functional operation. E.g., the next developer looking at your code may say, “hey that looks like we should use allMatch here”…
So I’d stay with the loop. But when processing the associations of a Map, you should not loop over the entrySet(), to perform a lookup for every key, but rather use
boolean isValid = true;
for(Map.Entry<String, ValueType> e: aMap.entrySet())
isValid &= validate(e.getKey(), e.getValue());
Starting with Java 10, you may use
boolean isValid = true;
for(var e: aMap.entrySet()) isValid &= validate(e.getKey(), e.getValue());
eliminating the most annoying syntactical element of that loop.
回答2:
The issue with aMap.forEach((k,v) -> isValid &= validate(k, v)); is that the variable captured in a lambda expression should be final or effectively final. This is the same for anonymous inner classesin Java.
So, to answer your query, you can convert isValid to a final one element array, like this:
final boolean[] isValid = {true}; and then aMap.forEach((k,v) -> isValid[0] &= validate(k, v));
You can also probably make it atomic, but that would require more code changes. This solution is much simpler.
来源:https://stackoverflow.com/questions/51426673/java-8-iterating-across-all-elements-of-a-map