如何在继续流式传输时获取第一个元素?

我有一个通用项目流。我想打印第一个项目的类名 + 所有项目的类名。toString()

如果我有一个可迭代的,它看起来像这样:

Iterable<E> itemIter = ...;
boolean first = true;
for (E e : itemIter) {
    if (first) {
        first = false;
        System.out.println(e.getClass().getSimpleName());
    }
    System.out.println(e);
}

是否可以使用流 API 在流 () 上执行此操作?Stream<T>

*请注意,这是一个关于流的问题 - 而不是关于迭代器的问题。我有一个流 - 不是迭代器。


答案 1

StreamEx 库扩展了标准的 Java 的 Stream API。使用 StreamEx.of(迭代器)peekFirst

StreamEx.of(itemIter.iterator())
        .peekFirst(e -> System.out.println(e.getClass().getSimpleName()))
        .forEach(System.out::println);

答案 2

如果您的起点是 a,并且您希望保留其所有属性和懒惰,则以下解决方案将起作用:Stream

public static <E> Stream<E> forFirst(Stream<E> stream, Consumer<? super E> c) {
    boolean parallel = stream.isParallel();
    Spliterator<E> sp = stream.spliterator();
    return StreamSupport.stream(() -> {
        if(sp.getExactSizeIfKnown() == 0) return sp;
        Stream.Builder<E> b = Stream.builder();
        if(!sp.tryAdvance(b.andThen(c))) return sp;
        return Stream.concat(b.build(), StreamSupport.stream(sp, parallel)).spliterator();
    }, sp.characteristics(), parallel);
}

例如,当您将其与

List<String> list = new ArrayList<>(List.of("foo", "bar", "baz"));
Stream<String> stream = forFirst(
        list.stream().filter(s -> s.startsWith("b")),
        s -> System.out.println(s+" ("+s.getClass().getSimpleName()+')')
    ).map(String::toUpperCase);
list.add(1, "blah");
System.out.println(stream.collect(Collectors.joining(" | ")));

它将打印

blah (String)
BLAH | BAR | BAZ

证明在开始终端操作之前不会开始处理(),从而反映对源的先前更新。collectList