I'm struggling to find a way to skip some elements at the beginning of a stream depending on a predicate.
Something like this:
dropWhile( n -> n < 3, Stream.of( 0, 1, 2, 3, 0, 1, 2, 3, 4 ) )
.forEach( System.out::println );
3
0
1
2
3
4
That is the equivalent of Scala dropWhile
.
As per the Scala documentation, the definition of the dropWhile method is as follows: The dropWhile method is a member of the IterableLike trait. 1. How to initialize a Sequence of donuts The code below shows how to initialize a Sequence of Donut elements of type String.
The correct technical way to think about this is that a Scala method name that ends with the : character is right-associative, meaning that the method comes from the variable on the right side of the expression. Therefore, with +: and ++:, these methods comes from the Seq that’s on the right of the method name.
The dropWhile method takes a predicate function parameter that will be used to drop certain elements in a collection which satisfies the predicate function. The "dropping" process, or removal of elements, will stop as soon as it encounters an element that does not match the predicate function.
Because the Scala Seq is immutable, you can’t add elements to an existing Seq. The way you work with Seq is to modify the elements it contains as you assign the results to a new Seq. These examples show how to use the append and prepend methods: Note that during these operations the : character is always next to the old (original) sequence.
Unfortunately, the only way to do that with Java 8 is with the solution provided by Holger.
However, the operation dropWhile(predicate)
has been added to Java 9 so starting from JDK 9, you can simply have:
Stream.of(0, 1, 2, 3, 0, 1, 2, 3, 4).dropWhile(n -> n < 3).forEach(System.out::println);
This kind of operation is not an intended use case for Stream
s as it incorporates a dependency between the elements. Therefore the solution might not look elegant as you have to introduce a state-full variable for your predicate:
class MutableBoolean { boolean b; }
MutableBoolean inTail = new MutableBoolean();
IntStream.of(0, 1, 2, 3, 0, 1, 2, 3, 4)
.filter(i -> inTail.b || i >= 3 && (inTail.b = true))
.forEach(System.out::println);
Note that the condition had to be reversed compared to your example.
Of course, you can hide the nasty details in a method:
public static void main(String... arg) {
dropWhile(n -> n < 3, Stream.of(0, 1, 2, 3, 0, 1, 2, 3, 4))
.forEach(System.out::println);
}
static <T> Stream<T> dropWhile(Predicate<T> p, Stream<T> s) {
class MutableBoolean { boolean b; }
MutableBoolean inTail = new MutableBoolean();
return s.filter(i -> inTail.b || !p.test(i) && (inTail.b = true));
}
A more complex, but cleaner and potentially more efficient way is to go down to the metal, i.e the Spliterator
interface:
static <T> Stream<T> dropWhile(Predicate<T> p, Stream<T> s) {
Spliterator<T> sp = s.spliterator();
return StreamSupport.stream(new Spliterators.AbstractSpliterator<T>(
sp.estimateSize(), sp.characteristics() & ~Spliterator.SIZED) {
boolean dropped;
public boolean tryAdvance(Consumer<? super T> action) {
if(dropped) return sp.tryAdvance(action);
do {} while(!dropped && sp.tryAdvance(t -> {
if(!p.test(t)) {
dropped=true;
action.accept(t);
}
}));
return dropped;
}
public void forEachRemaining(Consumer<? super T> action) {
while(!dropped) if(!tryAdvance(action)) return;
sp.forEachRemaining(action);
}
}, s.isParallel());
}
this method can be used the same way as the first dropWhile
method, but it will work even with parallel streams, though not as efficient as you might wish.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With