Getting sublist from a Java list with nth elements that fulfills a condition with streams

21,869

If your input is a List with fast random access, you can solve your problem using the stream of indices:

public static List<String> f(int n, List<String> input) {
    int fence = IntStream.range(0, input.size())
                         .filter(idx -> input.get(idx).equals("B")) // leave only B's
                         .skip(n-1)
                         .findFirst() // an index of n-th B
                         .getAsInt(); // with throw NoSuchElementException if not enough B's
    return input.subList(0, fence+1);
}

Usage example:

System.out.println(f(3, Arrays.asList("A", "A", "A", "B", "A", "B", "A", "B", "A", "A")));
System.out.println(f(2, Arrays.asList("A", "A", "A", "B", "A", "B", "A", "B", "A", "A")));
System.out.println(f(1, Arrays.asList("A", "A", "A", "B", "A", "B", "A", "B", "A", "A")));

However despite I love Stream API I would solve this problem imperatively.

Share:
21,869
user3483969
Author by

user3483969

Updated on January 28, 2020

Comments

  • user3483969
    user3483969 over 4 years

    I have a very simple use case

    Given a list of letters with A and Bs, I want to get the sublist that contains the first N Bs, for example:

    • f(3, [A A A B A B A B A A]) = [A A A B A B A B]
    • f(2, [A A A B A B A B A A]) = [A A A B A B]
    • f(1, [A A B A B A]) = [A A B]
    • f(0, [A A B A B]) = []

    By following an imperative approach, this is relatively easy, count until we find N Bs, and then get the sublist until that position.

    However, I couldn't find any functional solution with lambdas, since the operation on every node seems to be independent from the others (which I guess make sense for parallelization).

  • user3483969
    user3483969 over 8 years
    Thanks Tagir, that is exactly what I was looking for. In any case, I have to agree with you, in this case the imperative approach seems to be much more explanatory, so I will leave it like now in the code.
  • user3483969
    user3483969 over 8 years
    However, your help is very valuable to go deeper in the functional understanding of the language.
  • Tagir Valeev
    Tagir Valeev over 8 years
    Note that your solution violates the contract of filter method: the supplied predicate must be stateless.
  • Karol Król
    Karol Król over 8 years
    @TagirValeev Not "must be" but "should be". This solution doesn't broke any restriction. Used operations on AtomicInteger are synchronized (and atomic). Anyway in this case there is no option to use parallelStream but simple stream - which use one thread. Do not be so orthodox - Java 8 this is not real functional programming language.
  • user3483969
    user3483969 over 8 years
    @KarolKról Thanks for your alternative. Even if it is not sure if it violates the contract of filter or not, I feel it carries a little bit more complexity (ie an AtomicInteger should be used instead of a regular one). In the end, I think that the imperative approach is the clearer in this case
  • Karol Król
    Karol Król over 8 years
    @user3483969 I agree with you, but IMO Java Stream API is poor if you compare it with similar Scala API. Moreover TagirValeev approach is also not "so imperative" because it read/compare elements of mutable collection :/ In fact is even worse because get method on List are not atomic - I just defend my super solution :P Vote up man ;) youtube.com/watch?v=REUukm_WQJI
  • user3483969
    user3483969 over 8 years
    @KarolKról Yep, although I like having the possibility to write more "functional" style code, the API is not so flexible as Scala`s.