Ответ 1
Этот тип операций не предназначен для использования для Stream
, поскольку он включает зависимость между элементами. Поэтому решение может выглядеть не изящно, поскольку вам нужно ввести переменную состояния для вашего предиката:
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);
Обратите внимание, что условие должно быть отменено по сравнению с вашим примером.
Конечно, вы можете скрыть неприятные данные в методе:
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));
}
Более сложным, но более чистым и потенциально более эффективным способом является спуск на металл, то есть интерфейс Spliterator
:
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());
}
этот метод можно использовать так же, как и первый метод dropWhile
, но он будет работать даже с параллельными потоками, хотя и не так эффективен, как вы могли бы пожелать.