如何检查Java 8stream是否为空?
如何检查Stream
是否为空,如果不是,则作为非terminal操作抛出exception?
基本上,我正在寻找相当于下面的代码,但没有实现中间的stream。 特别是,在stream被terminal操作实际消耗之前,检查不应该发生。
public Stream<Thing> getFilteredThings() { Stream<Thing> stream = getThings().stream() .filter(Thing::isFoo) .filter(Thing::isBar); return nonEmptyStream(stream, () -> { throw new RuntimeException("No foo bar things available") }); } private static <T> Stream<T> nonEmptyStream(Stream<T> stream, Supplier<T> defaultValue) { List<T> list = stream.collect(Collectors.toList()); if (list.isEmpty()) list.add(defaultValue.get()); return list.stream(); }
如果您可以使用有限的并行function,则以下解决scheme将起作用:
private static <T> Stream<T> nonEmptyStream( Stream<T> stream, Supplier<RuntimeException> e) { Spliterator<T> it=stream.spliterator(); return StreamSupport.stream(new Spliterator<T>() { boolean seen; public boolean tryAdvance(Consumer<? super T> action) { boolean r=it.tryAdvance(action); if(!seen && !r) throw e.get(); seen=true; return r; } public Spliterator<T> trySplit() { return null; } public long estimateSize() { return it.estimateSize(); } public int characteristics() { return it.characteristics(); } }, false); }
以下是使用它的一些示例代码:
List<String> l=Arrays.asList("hello", "world"); nonEmptyStream(l.stream(), ()->new RuntimeException("No strings available")) .forEach(System.out::println); nonEmptyStream(l.stream().filter(s->s.startsWith("x")), ()->new RuntimeException("No strings available")) .forEach(System.out::println);
(高效)并行执行的问题是,支持Spliterator
分割需要线程安全的方式来注意是否任何一个片段以线程安全的方式看到任何值。 然后执行tryAdvance
的最后一个片断必须认识到它是最后一个(也不能提前)抛出适当的exception。 所以我没有在这里join分裂的支持。
其他答案和评论是正确的,因为要检查一个stream的内容,必须添加一个terminal操作,从而“消耗”stream。 然而,可以做到这一点,并把结果变成stream,而不缓冲stream的全部内容。 这里有几个例子:
static <T> Stream<T> throwIfEmpty(Stream<T> stream) { Iterator<T> iterator = stream.iterator(); if (iterator.hasNext()) { return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false); } else { throw new NoSuchElementException("empty stream"); } } static <T> Stream<T> defaultIfEmpty(Stream<T> stream, Supplier<T> supplier) { Iterator<T> iterator = stream.iterator(); if (iterator.hasNext()) { return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false); } else { return Stream.of(supplier.get()); } }
基本上把这个stream变成一个Iterator
来调用hasNext()
,如果是true,把Iterator
变成一个Stream
。 这是低效率的,因为stream中的所有后续操作都将通过迭代器的hasNext()
和next()
方法,这也意味着stream有效地按顺序处理(即使后来变为并行)。 但是,这确实允许您testingstream而不缓冲其所有元素。
有可能是使用Spliterator
而不是Iterator
来做到这一点。 这可能允许返回的stream具有与inputstream相同的特性,包括并行运行。
您必须在Stream上执行terminal操作,才能应用任何filter。 因此,直到消耗完它才能知道它是否为空。
你可以做的最好的办法就是用findAny()
terminal操作来终止Stream,当它find任何元素的时候它将停止,但是如果没有的话,它将不得不迭代所有的input列表来find它。
如果input列表中包含许多元素,并且前几个filter之一,这只会帮助您,因为在知道stream不是空的之前,只需要使用列表的一小部分。
当然,你仍然需要创build一个新的Stream来产生输出列表。
按照斯图尔特的想法,这可以用这样的Spliterator
来完成:
static <T> Stream<T> defaultIfEmpty(Stream<T> stream, Stream<T> defaultStream) { final Spliterator<T> spliterator = stream.spliterator(); final AtomicReference<T> reference = new AtomicReference<>(); if (spliterator.tryAdvance(reference::set)) { return Stream.concat(Stream.of(reference.get()), StreamSupport.stream(spliterator, stream.isParallel())); } else { return defaultStream; } }
我认为这与平行stream一起工作,因为stream.spliterator()
操作将终止stream,然后根据需要重build它
在我的用例中,我需要一个默认Stream
而不是默认值。 如果这不是你所需要的,那很容易改变