从流中收集某些元素

ste*_*fen 6 java java-8 java-stream

如何通过位置从Stream中提取两个元素?例如,我试图从a中提取元素0和1(这些数字是任意的!)Stream<String>.一个天真的方法是:

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();
Run Code Online (Sandbox Code Playgroud)

这会产生以下输出:

c0.accept(s0)
c0.accept(s1)
c1.accept(s1)
Run Code Online (Sandbox Code Playgroud)

我明白这是因为s0它将进入流,遇到skip(0),然后peek(c0)(它给出第一行)然后skip(1),它将跳过这个元素,然后显然继续从流的开头的下一个元素.

我以为我可以使用这些消费者来提取字符串,但c0会被第二个元素覆盖:

String[] extracted = new String[2];
c0 = s -> extracted[0];
c1 = s -> extracted[1];
Run Code Online (Sandbox Code Playgroud)

编辑:

这些是流的特征:

  • 只有一个流,而不是列表或数组
  • 流可能是无限的
  • 流可以按顺序进行

Tag*_*eev 4

鉴于您的限制,您可以将limit()与自定义收集器结合起来,如下所示:

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));
}
Run Code Online (Sandbox Code Playgroud)

这Set<Integer> wantedIndices是包含所需元素索引的集合(不限于 2)。用法:

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]
Run Code Online (Sandbox Code Playgroud)