Reputation: 2235
I understand why lambda expressions can't mutate local variables, and I understand that they can access and mututae class fields. For me, lambdas create much more expressive code in many situations and I would like to use them more often.
My question is what the the best practices(s) for using lambda not as single-method object replacements, but to instead simplify code such as this:
int pairsFound = 0;
for (Map.Entry<String, Integer> entry : counters.entrySet())
{
if (entry.getValue() == 2)
pairsFound++;
}
to
pairsFound = map.forEach((key, value) ->
{
int z = 0;
if (value == 2)
z++;
return z;
});
or
map.forEach((key, value) ->
{
if (value == 2)
pairsFound++;
});
One solution is to make parisFound
an instance field. But instatizing variables that are never used outside of a single method seems like a code smell to me. Also, this won't work if the enclosing class is static (such as in a test run).
Are there other ways of getting information out of a lambda in a way that isn't any less safe than using a for each loop? I understand that there can be concurrency issues with lambdas, but in most of my simplifying use cases I don't know if the lambda would be any less safe than what it is replacing.
Upvotes: 4
Views: 2389
Reputation: 49764
The simplest way for this scenario is to create a stream, filter on the condition of entry.getValue() == 2
, and then count()
the result. Something like this:
long pairsFound = map.values().stream().filter(v -> v == 2).count(); //using values() rather than entrySet() makes the code more readable
The general lesson is that using lambdas requires a different mindset, one closer to functional programming (where the concept of lambdas originates), rather than trying to impose the same imperative principles on it. The correct way to get information out of lambdas is by evaluating them, not by a side effect.
Upvotes: 10
Reputation: 39406
To keep with your current implementation, you'd use map
instead of forEach
, to return a value, and then reduce
to accumulate:
pairsFound = map.values().stream()
.map(value -> value == 2 ? 1 : 0)
.reduce(Integer::sum);
Upvotes: 4