-2

Is there an alternative to lambda expressions? With my current code, I can't access the variable maxCnt, modify it, and send it back out to use.

int maxCnt = 0;

incoming.stream().forEach(a -> {
    Integer cnt = wordFrequency.get(a);
    if (cnt != null) {
        if (cnt > maxCnt) {
            maxCnt = cnt;
        }
    }

});

Naman
  • 27,789
  • 26
  • 218
  • 353
  • 1
    That's because you're using forEach() instead of map(), filter() and max().https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#max-java.util.Comparator- – JB Nizet Oct 25 '19 at 22:38
  • Do take a look at [Variable used in lambda expression should be final or effectively final](https://stackoverflow.com/questions/34865383/variable-used-in-lambda-expression-should-be-final-or-effectively-final) – Naman Oct 26 '19 at 04:21
  • 1
    Streams should be free from side effects. So when you think you have a need to modify a variable outside the stream, it’s a sign of a bad design (and there are tricks to do it if you insist; however the answers already show a couple of good designs, so in this case you shouldn’t). – Ole V.V. Oct 26 '19 at 06:46
  • As an aside, assuming that `incoming` is a collection you don’t need a stream. You might have done just `incoming.forEach`. – Ole V.V. Oct 26 '19 at 06:48

2 Answers2

5

Sure thing, you can do:

int maxCnt = incoming.stream()
          .map(wordFrequency::get)
          .filter(Objects::nonNull)
          .mapToInt(Integer::intValue)
          .max().orElse(0);
Naman
  • 27,789
  • 26
  • 218
  • 353
Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
  • 4
    `wordFrequency::get`? – Elliott Frisch Oct 25 '19 at 22:42
  • How about `int maxCnt = incoming.stream() .mapToInt(in -> wordFrequency.getOrDefault(in, 0)) .summaryStatistics().getMax();` for an alternative? – Naman Oct 26 '19 at 05:13
  • 1
    @Naman it will be wrong if all the values are negative, and it will compute more statistics for nothing. – JB Nizet Oct 26 '19 at 06:37
  • @JBNizet for all negative values, even the current logic in the question and the answer would be wrong. [...and now that I realize, it would actually be inconsistent when the `incoming` is actually empty!!] Compute was something I was also thinking about(when I posted it as a question in comments) and I do second the thought that Optional with orElse could be a preferred choice there. – Naman Oct 26 '19 at 08:29
4

Nothing requires a lambda. In your case, a for-loop can do the same thing:

int maxCnt = 0;
for (var a : incoming) {
    Integer cnt = wordFrequency.get(a);
    if (cnt != null && cnt > maxCnt) maxCnt = cnt;
}

This is readable and performs well; you aren't losing anything by not using a lambda—today.

In the future, with advances in the performance of parallel streams maybe it will become worthwhile to run even small workloads in parallel. In other words, more streams could default to being parallel instead of sequential. In such a future, using a Stream and lambdas instead of this for-loop would allow your code to take advantage of those improvements without a rewrite.

erickson
  • 265,237
  • 58
  • 395
  • 493