java - procesamiento - Omita los Ășltimos x elementos en Stream<T>
stream java 8 ejemplo (2)
Si tengo un Stream<T>
, puedo usar skip(long)
fácilmente para omitir los primeros elementos de un stream. Sin embargo, parece que no hay un equivalente para saltar una cantidad determinada de elementos al final de la transmisión.
La solución más obvia es usar limit(originalLength - elementsToRemoveAtEnd)
, pero eso requiere conocer la longitud inicial de antemano, lo que no siempre es el caso.
¿Hay alguna manera de eliminar los últimos pocos elementos de una secuencia de longitud desconocida sin tener que recopilarla en una Collection
, contar los elementos y volver a transmitirla?
No hay una solución general sin almacenamiento para Stream
que puede tener una longitud desconocida. Sin embargo, no es necesario que recopile toda la transmisión, solo necesita un espacio de almacenamiento tan grande como la cantidad de elementos que desea omitir:
static <T> Stream<T> skipLastElements(Stream<T> s, int count) {
if(count<=0) {
if(count==0) return s;
throw new IllegalArgumentException(count+" < 0");
}
ArrayDeque<T> pending=new ArrayDeque<T>(count+1);
Spliterator<T> src=s.spliterator();
return StreamSupport.stream(new Spliterator<T>() {
public boolean tryAdvance(Consumer<? super T> action) {
while(pending.size()<=count && src.tryAdvance(pending::add));
if(pending.size()>count) {
action.accept(pending.remove());
return true;
}
return false;
}
public Spliterator<T> trySplit() {
return null;
}
public long estimateSize() {
return src.estimateSize()-count;
}
public int characteristics() {
return src.characteristics();
}
}, false);
}
public static void main(String[] args) {
skipLastElements(Stream.of("foo", "bar", "baz", "hello", "world"), 2)
.forEach(System.out::println);
}
El siguiente código usa ArrayDeque
para almacenar en el búfer n
elementos donde n
es la cantidad de elementos para omitir al final. El truco es usar skip(n)
. Esto hace que los primeros n
elementos se agreguen a ArrayDeque
. Luego, una vez que se han almacenado n
elementos en la memoria, la transmisión continúa procesando los elementos, pero ArrayDeque
elementos de ArrayDeque
. Cuando se llega al final de la secuencia, los últimos n
elementos se atascan en ArrayDeque
y se descartan.
ArrayDeque
no permite elementos null
. El código siguiente asigna null
a NULL_VALUE
antes de agregar a ArrayDeque
y luego asigna NULL_VALUE
nuevo a null
después de hacer popping desde ArrayDeque
.
private static final Object NULL_VALUE = new Object();
public static <T> Stream<T> skipLast(Stream<T> input, int n)
{
ArrayDeque<T> queue;
if (n <= 0)
return(input);
queue = new ArrayDeque<>(n + 1);
input = input.
map(item -> item != null ? item : NULL_VALUE).
peek(queue::add).
skip(n).
map(item -> queue.pop()).
map(item -> item != NULL_VALUE ? item : null);
return(input);
}