28

My POJO is as follows

class EventUser {
  private id;
  private userId;
  private eventId;
}

I retrieve EventUser object as follows:

List<EventUser> eventUsers = eventUserRepository.findByUserId(userId);

Say the 'eventUsers' is as follows:

[
{"id":"id200","userId":"001","eventId":"1010"},
{"id":"id101","userId":"001","eventId":"4212"},
{"id":"id402","userId":"001","eventId":"1221"},
{"id":"id301","userId":"001","eventId":"2423"},
{"id":"id701","userId":"001","eventId":"5423"},
{"id":"id601","userId":"001","eventId":"7423"}
]

Using streaming, and without using any intermediate variable , how can I filter and collect events after a given EventUser.id: ex:

List<EventUser> filteredByOffSet = eventUsers.stream.SOMEFILTER_AND_COLLECT("id301");

the result should be :

[{"id":"id301","userId":"001","eventId":"2423"},
{"id":"id701","userId":"001","eventId":"5423"},
{"id":"id601","userId":"001","eventId":"7423"}]
Hulk
  • 6,399
  • 1
  • 30
  • 52
Ashika Umanga Umagiliya
  • 8,988
  • 28
  • 102
  • 185

4 Answers4

25

In Java 8 you need a stateful filter

public static <T> Predicate<T> from(Predicate<T> test) {
    boolean[] found = { false };
    // once found, always true
    return t -> found[0] || (found[0] = test.test(t));
}

NOTE: this only makes sense for single threaded streams.

List<EventUser> filteredByOffSet = 
     eventUsers.stream()
               .filter(from(e -> "id301".equals(e.getId()))
               .collect(Collectors.toList());
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • 2
    I like this one - it's a lot more concise than [your answer on this question](https://stackoverflow.com/a/35791245/2513200) – Hulk Sep 11 '18 at 07:36
  • @Hulk I had completely forgotten this answer. Thank you for the link. – Peter Lawrey Sep 11 '18 at 07:42
  • 1
    @PeterLawrey yeah i prefer this ! – Ashika Umanga Umagiliya Sep 11 '18 at 07:45
  • 5
    @PeterLawrey this is a *really* bad idea, I would at least force `sequential` here – Eugene Sep 11 '18 at 08:34
  • 2
    @PeterLawrey btw [there is back-port for java-8 for dropWhile](https://stackoverflow.com/a/25570609/1059372) – Eugene Sep 11 '18 at 08:38
  • 2
    This does not work as written. Variables in a lambda need to be `final` or effectively final. The `found` variable can't be modified inside the lambda. It needs to be wrapped in a `boolean[1]` or `AtomicBoolean` or similar. – Christoffer Hammarström Sep 11 '18 at 11:02
  • @Eugene sequential is the default. – Peter Lawrey Sep 12 '18 at 08:59
  • 1
    @AshikaUmangaUmagiliya some one edited the code and broke it but it has been reverted. – Peter Lawrey Sep 12 '18 at 08:59
  • 1
    @PeterLawrey I know, it's just to make the reader aware that this is *really* just for sequential streams, I guess – Eugene Sep 12 '18 at 09:31
  • This is wrong(by its nature) *boolean[] found = { false };* Please edit this answer to use either AtomicReferrence or AtomicBoolean... – dbl Sep 19 '18 at 15:31
  • 1
    @dbl AtomicReference and AtomicBoolean suggest thread safety when none exists. Using these classes are more likely to lead to incorrect usage IMHO. BTW You can quote code by using ` and ` around the code – Peter Lawrey Sep 19 '18 at 15:51
  • @PeterLawrey what I meant is - this code is such that most of my colleagues will not approve for merging. I've tried using same workaround for lambdas but the feedback usually will be - "looks tricky as hell and draws the attention of the reviewer in the wrong direction. Requires deeper understanding of lambdas." Proposal in such cases will be either a value wrapper or use of another utillisation rather than streams. Thank you for pointing out how to quote code inside comments :) Never did my homework checking any of those though. – dbl Sep 20 '18 at 08:00
  • 1
    @dbl either way you would be using a lambda. I guess it requires a deeper knowledge of arrays which isn't as wide as it could be. – Peter Lawrey Sep 20 '18 at 11:43
23

Use "dropWhile" from Java 9.

Dmitry Gorkovets
  • 2,208
  • 1
  • 10
  • 19
  • @AshikaUmangaUmagiliya thx to all mighty Holger this is possible in java-8 too. Dmitry just link that to your answer and this should be the accepted one IMO. [here is the link](https://stackoverflow.com/a/25570609/1059372) – Eugene Sep 11 '18 at 08:37
  • 10
    Please add an example using `dropWhile` in your answer. Right now this is a link-only answer and at risk of being deleted :) – Jesse Sep 11 '18 at 13:53
  • 1
    @DmitryGorkovets consider adding the stream should be ordered when using this method. If it's unordered the behavior is non-deterministic. – Brad Cupit Sep 11 '18 at 14:19
  • 4
    @AshikaUmangaUmagiliya if you're stuck with Java8, I recommend StreamEx library which offers dropWhile (among many others). – Jean-François Savard Sep 11 '18 at 14:58
16

Find the index of the search item first:

int asInt = IntStream.range(0, list.size())
    .filter(userInd-> list.get(userInd).equals(<criteria>))
    .findFirst()
    .getAsInt();

Get items on and after the index:

list.stream().skip(asInt).collect(Collectors.toList());
S.K.
  • 3,597
  • 2
  • 16
  • 31
  • 1
    This should be the accepted answer, though I'd use `List.subList(from, to)` instead of skip – fps Sep 11 '18 at 12:15
  • @FedericoPeraltaSchaffner Why go through the work of creating a sub list when you can just skip? – Tim B Sep 11 '18 at 12:28
  • 1
    @TimB subList doesn't create a new list, it's just a view of a range of the original list – fps Sep 11 '18 at 12:33
  • @FedericoPeraltaSchaffner And the view is an object. I agree it's not as much work as copying the list, it's still greater than zero. – Tim B Sep 11 '18 at 14:51
  • @TimB maybe I wasn't clear enough. I meant subList instead of stream, skip and collect to a new list – fps Sep 11 '18 at 14:56
  • @FedericoPeraltaSchaffner Oh right. Yes that's a significantly more efficient solution (so long as the original list is never modified) - it doesn't use streaming though which this question specifically asked for. – Tim B Sep 11 '18 at 16:16
1

You cant do that without using any intermediate variables. finding the position and iterate it to the end (see this question below that answer it more precisely) enter link description here

jdev
  • 5,304
  • 1
  • 17
  • 20