2

I had a cycle for counter:

List<Mt4Report> history = ...
int counter = 0;
for (Mt4Report item : history) {
    if (item.getProfit().compareTo(BigDecimal.ZERO) < 0) {
        counter++;
    } else {
        break;
    }
}

How I can write the same idea with lambda expression something .findFirst().ifPresent but withholding break statement?

Naman
  • 27,789
  • 26
  • 218
  • 353
Pavel
  • 2,005
  • 5
  • 36
  • 68
  • 1
    i will say you have the clear version according to java 8 – Ryuzaki L Nov 07 '19 at 18:22
  • Why not filter the stream based on `item.getProfit().compareTo(BigDecimal.ZER) < 0` and then just call `count()`? – ahoxha Nov 07 '19 at 18:41
  • @ahoxha that's exactly what I did but Pavel requires that the stream terminates when the condition in the `filter` is false for which I have answered in context of Java 8 – Shankha057 Nov 07 '19 at 18:44
  • When you use streams, you don't tell them how to perform the task, but rather what task to perforn. I am not sure if the order is important, but if you want to find all items with profit less then zero, than you just filter the stream and call `count()`. On the other hand, if he is breaking the loop on the first non-zero item (which could omit other zero items later on the list), I don't understand the logic. – ahoxha Nov 07 '19 at 18:48
  • @ahoxha it may be a fail-fast logic for something where you expect all profits to be zero but even if one non zero item is encountered then simply break and return the number of zero items – Shankha057 Nov 07 '19 at 18:52
  • @Shankha057, yes you're right, it might be something like that. – ahoxha Nov 07 '19 at 18:54

2 Answers2

4

With Java-9 and above, you can use takeWhile approach as :

int counter = (int) history.stream()
        .takeWhile(item -> item.getProfit().compareTo(BigDecimal.ZERO) < 0)
        .count();

For the Java-8 solution, you can look into a custom implementation of takeWhile provided in this answer. On the other hand, a less efficient implementation with the use of indexOf could be to perform:

int count = history.stream()
        .filter(ite -> ite.getProfit().compareTo(BigDecimal.ZERO) >= 0)
        .findFirst()
        .map(history::indexOf)
        .orElse(history.size());

As Holger suggested to improve the above solution, you can make use of the IntStream with findFirst:

int count = IntStream.range(0, history.size())
                     .filter(ix -> history.get(ix).getProfit() .compareTo(BigDecimal.ZERO) >= 0)
                     .findFirst()
                     .orElse(history.size());
Naman
  • 27,789
  • 26
  • 218
  • 353
  • Yes, the good solution very compact and readable but it for 9 version my question was about version 8. – Pavel Nov 07 '19 at 18:15
  • I think your java8 code is not correct, as @Pavel wants to find items with profit < 0, not >= 0. – ahoxha Nov 07 '19 at 18:44
  • @ahoxha It's just the logic reversed, notice the `indexOf` for the first non-fitting element as a result. – Naman Nov 07 '19 at 18:48
  • Sorry, my bad. @Pavel, I think Nama's java8 version is the one you are looking for. – ahoxha Nov 07 '19 at 18:57
  • `IntStream.range(0, history.size()) .filter(ix -> history.get(ix).getProfit() .compareTo(BigDecimal.ZERO) >= 0) .findFirst() .orElse(history.size());` avoids the second linear search through the list. – Holger Nov 08 '19 at 09:53
1

As per Java 8 there is no direct solution for this problem, which is basically stopping a Stream using a Predicate.
In Java 9 you have the takeWhile() method but in Java 8, there is no such thing like that.
Please refer to this post

Shankha057
  • 1,296
  • 1
  • 20
  • 38