Reputation: 86747
I have a list of elements, and want to extract the value of the fields' propery. Problem: all elements should have the same property value.
Can I do better or more elegant than the following?
Set<String> matches = fields.stream().map(f -> f.getField()).collect(Collectors.toSet());
if (matches.size() != 1) throw new IllegalArgumentException("could not match one exact element");
String distrinctVal = matches.iterator().next(); //continue to use the value
Is this possible directly using the stream methods, eg using reduce
?
Upvotes: 8
Views: 1176
Reputation: 86276
As others have said, it’s largely a matter of taste. Here’s mine.
String distinctVal = fields.iterator().next().getField();
if (fields.stream().map(Field::getField).anyMatch(e -> ! e.equals(distinctVal)) {
throw new IllegalArgumentException("could not match one exact element");
}
//continue to use the value
(Code is not tested; forgive typos.)
I didn’t particularly code with performance efficiency in mind, but the code will only search until the first non-matching string, so should be efficient.
Upvotes: 0
Reputation: 4120
Similar to this:
String distrinctVal = fields.stream()
.map(f -> f.getField())
.reduce((a, b)
-> { throw new IllegalArgumentException("could not match one exact element");}
).get();
Upvotes: 0
Reputation: 18430
Your current solution is good. You can try this way also to avoid collecting.
Use distinct()
then count()
if (fields.stream().map(f -> f.getField()).distinct().count() != 1)
throw new IllegalArgumentException("could not match one exact element");
To get the value
String distrinctVal = fields.get(0).getField();
Upvotes: 5
Reputation: 56433
Well you could certainly do this in several ways but as to which is more elegant can vary from person to person.
Anyway if you were to attempt this via streams this is how i would have done it:
With a slight modification to my answer here you could do:
boolean result = fields.stream()
.map(f -> f.getField())
.distinct()
.limit(2) // ENABLE SHORT CIRCUITING
.count() != 1;
if (result) throw new IllegalArgumentException("could not match one exact element");
String distinctVal = fields.get(0).getField();
The benefit of this approach is basically utilising limit(2)
to enable optimisation where possible.
Conclusion : your current approach is quite good actually so I wouldn't be surprised if you were to stick to that but you also have the choice of this approach where you can short-circuit the pipeline.
Upvotes: 4
Reputation: 4368
That would be the reduce solution.
Optional<String> distinctVal = fields.stream()
.map(f -> f.getField())
.reduce((a, b) -> {
if(a != b) throw new IllegalArgumentException("could not match one exact element");
return a;
});
Upvotes: 3
Reputation: 2930
Depending on the frequency of invocation and the size of your set, the iterative code can be significantly faster.
public boolean allEqual(Collection<Fields> fields) {
if (fields.size() > 1) {
String last;
boolean first = true;
for (Field field : fields) {
String thisString = field.getField();
if (first) {
last = thisString;
} else {
if (!StringUtils.equals(last, thisString)) {
return false;
}
}
}
}
return true;
}
While this is not a streaming solution, it aborts when the first mismatch is found, which - depending on the input - can be significantly faster.
Upvotes: 0