Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Collect certain elements from stream

How can I extract two elements from a Stream by their positions? For example I'm trying to extract element 0 and 1 (these numbers are arbitrary!) from a Stream<String>. A naive approach is this:

List<String> strings = Arrays.asList("s0", "s1", "s2", "s3", "s4");
Consumer<String> c0 = s -> System.out.println("c0.accept(" + s + ")");
Consumer<String> c1 = s -> System.out.println("c1.accept(" + s + ")");
strings.stream().skip(0).peek(c0).skip(1).peek(c1).findAny();

This produces the following output:

c0.accept(s0)
c0.accept(s1)
c1.accept(s1)

I understand that it's because s0 will enter the stream, encounter skip(0), then peek(c0) (which gives the the first line) and then skip(1), which will skip this element and then apparently continue with the next element from the beginning of the stream.

I thought I could use these consumers to extract the strings, but c0 would be overwritten by the second element:

String[] extracted = new String[2];
c0 = s -> extracted[0];
c1 = s -> extracted[1];

EDIT:

These are the characteristics of the stream:

  • There's a stream only, not a list or an array
  • The stream is possibly infinite
  • The stream can be made sequential
like image 565
steffen Avatar asked Sep 09 '15 14:09

steffen


People also ask

How do you find the element of a stream?

Using Stream findFirst() Method: The findFirst() method will returns the first element of the stream or an empty if the stream is empty. Approach: Get the stream of elements in which the first element is to be returned. To get the first element, you can directly use the findFirst() method.


1 Answers

Given your restriction you can combine the limit() with custom collector like this:

public static <T, A, R> Collector<T, ?, R> collectByIndex(Set<Integer> wantedIndices, 
                                                          Collector<T, A, R> downstream) {
    class Acc {
        int pos;
        A acc = downstream.supplier().get();
    }
    return Collector.of(Acc::new, (acc, t) -> {
        if(wantedIndices.contains(acc.pos++))
            downstream.accumulator().accept(acc.acc, t);
    }, (a, b) -> {throw new UnsupportedOperationException();}, // combining not supported
       acc -> downstream.finisher().apply(acc.acc));
}

Here Set<Integer> wantedIndices is the set containing the indices of wanted elements (not limited by 2). Usage:

Set<Integer> wantedIndices = new HashSet<>(Arrays.asList(1, 3));
Stream<String> input = Stream.of("s0", "s1", "s2", "s3", "s4");
List<String> result = input.limit(Collections.max(wantedIndices)+1)
            .collect(collectByIndex(wantedIndices, Collectors.toList()));
// [s1, s3]
like image 52
Tagir Valeev Avatar answered Sep 24 '22 07:09

Tagir Valeev