The correct (though not very easy) way to do this is to write your own Spliterator. The common algorithm is the following:
- Take the existing stream Spliterator using
stream.spliterator()
- Write your own Spliterator which may consume elements of existing one when advancing probably doing some additional operations.
- Create a new stream based on your spliterator via
StreamSupport.stream(spliterator, stream.isParallel())
- Delegate
close() call to original stream like .onClose(stream::close).
Writing good spliterator which parallelizes well is often very non-trivial task. However if you don't care about parallelization you may subclass AbstractSpliterator which is simpler. Here's an example how to write a new Stream operation which removes an element at given position:
public static <T> Stream<T> removeAt(Stream<T> src, int idx) {
Spliterator<T> spltr = src.spliterator();
Spliterator<T> res = new AbstractSpliterator<T>(Math.max(0, spltr.estimateSize()-1),
spltr.characteristics()) {
long cnt = 0;
@Override
public boolean tryAdvance(Consumer<? super T> action) {
if(cnt++ == idx && !spltr.tryAdvance(x -> {}))
return false;
return spltr.tryAdvance(action);
}
};
return StreamSupport.stream(res, src.isParallel()).onClose(src::close);
}
This is minimal implementation and it can be improved to show better performance and parallelism.
In my StreamEx library I tried to simplify the addition of such custom stream operations via headTail. Here's how to do the same using StreamEx:
public static <T> StreamEx<T> removeAt(StreamEx<T> src, int idx) {
// head is the first stream element
// tail is the stream of the rest elements
// want to remove first element? ok, just remove tail
// otherwise call itself with decremented idx and prepend the head element to the result
return src.headTail(
(head, tail) -> idx == 0 ? tail : removeAt(tail, idx-1).prepend(head));
}
You can even support chaining with chain() method:
public static <T> Function<StreamEx<T>, StreamEx<T>> removeAt(int idx) {
return s -> removeAt(s, idx);
}
Usage example:
StreamEx.of("Java 8", "Stream", "API", "is", "not", "great")
.chain(removeAt(4)).forEach(System.out::println);
Finally note that even without headTail there are some ways to solve your problems using StreamEx. To remove at specific index you may zip with increasing numbers, then filter and drop indexes like this:
StreamEx.of(stream)
.zipWith(IntStreamEx.ints().boxed())
.removeValues(pos -> pos == idx)
.keys();
To collapse adjacent repeats there's dedicated collapse method (it even parallelizes quite well!):
StreamEx.of(stream).collapse(Object::equals);
distinctsomehow removes adjacent duplicates, so apparently it is possible. but i agree that removing last element may be not defined properlydistinctis a simple algorithm, it works just like the Linuxuniqcommand. All you need to do is keep track of your previously seen value. If the current value is different, record it as your previous value. If it's the same, skip this element and continue on. At most, you are looking at two consecutive elements at any given time. Your requirements make presumptions about theStreamthat may not be true and you wouldn't be able to find out until you processed theStreams.distinctremoves all the duplicates but it collects the whole stream. removing adjacent elements in single-threaded processing is possible without collecting entire stream. how can i create such function using java8 api?