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

时间:2019-03-06 16:13:18

标签: java java-stream

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

如果我有一个Iterable,它将看起来像这样:

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>)上执行此操作吗?

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

7 个答案:

答案 0 :(得分:16)

StreamEx库可以扩展标准Java的Stream API。使用StreamEx.of(Iterator)peekFirst

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

答案 1 :(得分:10)

本机解决方案: Stream在Java中不可重用。这意味着,消费流只能完成一次。如果您从流中获得第一个元素,则可以对其进行一次以上的迭代。

解决方法是创建与第一个流相同的另一个流或获取第一个项目,然后创建一个流,类似这样:

Stream<E> stream = StreamSupport.stream(Spliterators.spliteratorUnknownSize(sourceIterator, Spliterator.ORDERED), false);
E firstElement = itemIter.next();
stream.foreach(...);

修改

实际上没有“复制”流的任何方法,您需要保留一个迭代器/集合。有关here的更多信息。当流耗尽时,当涉及到内存时,由于没有用处,因此可以由垃圾收集器进行收集。 Stream本身所占用的空间不会比其起源的迭代器多。请记住,流可能是无限的。当前操作的元素存储在内存中。

答案 2 :(得分:8)

如果您的起点是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

表明处理将不会在开始终端操作(collect)之前开始,因此反映了对源List的先前更新。

答案 3 :(得分:5)

您可以滥用减少量:

Stream<E> stream = ...;
System.out.println(stream
                   .reduce("",(out,e) -> 
                   out + (out.isEmpty() ? e.getClass().getSimpleName()+"\n" : "")
                   + e));

答案 4 :(得分:0)

您可以使用peek

AtomicBoolean first = new AtomicBoolean(true);
StreamSupport.stream(itemIter.spliterator(), false)
    .peek(e -> {
        if(first.get()) {
            System.out.println(e.getClass().getSimpleName());
            first.set(false);
        }
    })
    ...

答案 5 :(得分:0)

一种解决方法是这样-

import java.util.*; 
import java.util.stream.Collectors;
public class MyClass {
   static int i = 0;
   static int getCounter(){
       return i;
   }
   static void incrementCounter(){
       i++;
   }
    public static void main(String args[]) {
        List<String> list = Arrays.asList("A", "B", "C", "D", "E", "F", "G"); 
        List<String> answer = list.stream().filter(str -> {if(getCounter()==0) {System.out.println("First Element : " + str);} incrementCounter(); return true;}). 
        collect(Collectors.toList()); 
        System.out.println(answer); 
    }
}

输出:

First Element : A
[A, B, C, D, E, F, G]

答案 6 :(得分:0)

您还可以使用布尔原子引用:

            <CONTENT>
            <dms:ComplexResponse ErrorCode="430" ErrorDescription="null :  PrivacyUE Mancante" Return="false" xmlns:dms="http://dmsmanagerservice">
                <dms:Element Name="DMSVERSION">2.7</dms:Element>
            </dms:ComplexResponse>
        </CONTENT>