How to check if Java 8 thread is empty?

How can I check if Stream empty and throws an exception if this is not the case as an operation without a terminal?

Basically, I'm looking for something that is equivalent to the code below, but without materializing the flow between them. In particular, the check should not occur before the stream is actually consumed by the 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(); } 
+82
java java-8 java-stream
Oct 30 '14 at 9:18
source share
5 answers

If you can live with limited parallel capabilities, the following solution will work:

 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); } 

Here is a code usage example:

 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); 

The problem of (efficient) parallel execution is that in order to support Spliterator separation, a thread-safe way is needed to find out if any of the fragments saw any value in a thread-safe way. Then the last of the fragments executing tryAdvance should understand that it is the last (and it also could not advance) in order to throw the corresponding exception. Therefore, I did not add support for sharing here.

+20
Oct 30 '14 at 17:33
source share

Other answers and comments are true in that to check the contents of a stream, you must add a terminal operation, thereby โ€œconsumingโ€ the stream. However, you can do this and return the result back to the stream without buffering the entire contents of the stream. Here are some examples:

 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()); } } 

Basically translate the stream into Iterator to call hasNext() on it, and if true, return Iterator back to Stream . This is inefficient, since all subsequent operations on the stream will go through the Iterator hasNext() and next() methods, which also implies that the stream is efficiently processed sequentially (even if it later became parallel). However, this allows you to test a stream without buffering all of its elements.

There is probably a way to do this using Spliterator instead of Iterator . This potentially allows the returned stream to have the same characteristics as the input stream, including parallel operation.

+31
Oct 30 '14 at 17:20
source share

You must perform a terminal operation in the stream to apply any of the filters. Therefore, you cannot know if it will be empty until you destroy it.

The best you can do is to abort the stream using the terminal's findAny() operation, which will stop when it finds an element, but if it is not there, it will have to iterate over all the input to find it.

This will help you only if there are a lot of elements in the input list, and one of the first goes through filters, since only a small subset of the list should be used before you know that the stream is not empty.

Of course, you will need to create a new thread to create a list of results.

+12
Oct 30 '14 at 9:24
source share

Following Stuart's idea, this can be done using the Spliterator as follows:

 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; } } 

I think this works with parallel streams as the operation stream.spliterator() terminates the stream and then restores it as needed

In my use case, I needed the default value of Stream , not the default value. which is pretty easy to change if that's not what you need

+3
Jul 17 '17 at 9:45
source share

I think it should be enough to display a logical

In code, this is:

 boolean isEmpty = anyCollection.stream() .filter(p -> someFilter(p)) // Add my filter .map(p -> Boolean.TRUE) // For each element after filter, map to a TRUE .findAny() // Get any TRUE .orElse(Boolean.FALSE); // If there is no match return false 
+2
Jun 24 '19 at 21:19
source share



All Articles