¿Cómo puedo obtener el último elemento de una secuencia o lista en el siguiente código?
¿Dónde data.careas
está un List<CArea>
:
CArea first = data.careas.stream()
.filter(c -> c.bbox.orientationHorizontal).findFirst().get();
CArea last = data.careas.stream()
.filter(c -> c.bbox.orientationHorizontal)
.collect(Collectors.toList()).; //how to?
Como puede ver, conseguir el primer elemento, con un cierto filter
, no es difícil.
Sin embargo, obtener el último elemento en una sola línea es un verdadero dolor:
- Parece que no puedo obtenerlo directamente de a
Stream
. (Solo tendría sentido para flujos finitos) - También parece que no puede obtener cosas como
first()
ylast()
desde laList
interfaz, lo cual es realmente una molestia.
No veo ningún argumento para no proporcionar un método first()
y last()
en la List
interfaz, ya que los elementos allí están ordenados y, además, se conoce el tamaño.
Pero según la respuesta original: ¿Cómo obtener el último elemento de un finito Stream
?
Personalmente, esto es lo más cercano que pude conseguir:
int lastIndex = data.careas.stream()
.filter(c -> c.bbox.orientationHorizontal)
.mapToInt(c -> data.careas.indexOf(c)).max().getAsInt();
CArea last = data.careas.get(lastIndex);
Sin embargo, implica el uso de un indexOf
en cada elemento, lo que probablemente no es lo que generalmente desea, ya que puede afectar el rendimiento.
java
list
java-8
java-stream
skiwi
fuente
fuente
Iterables.getLast
que lleva Iterable pero está optimizado para trabajarList
. Una manía es que no tienegetFirst
. LaStream
API en general es horriblemente anal, omitiendo muchos métodos de conveniencia. LINQ de C #, por restricción, se complace en proporcionar.Last()
e incluso.Last(Func<T,Boolean> predicate)
, aunque también admite infinitos Enumerables.Stream
API no es completamente comparableLINQ
ya que ambas se realizan en un paradigma muy diferente. No es peor ni mejor, simplemente es diferente. Y definitivamente, algunos métodos están ausentes no porque los desarrolladores de Oracle sean incompetentes o malos :)Respuestas:
Es posible obtener el último elemento con el método Stream :: reduce . La siguiente lista contiene un ejemplo mínimo para el caso general:
Esta implementación funciona para todos los flujos ordenados (incluidos los flujos creados a partir de listas ). Para flujos desordenados , por razones obvias, no se especifica qué elemento se devolverá.
La implementación funciona tanto para flujos secuenciales como paralelos . Eso puede resultar sorprendente a primera vista y, lamentablemente, la documentación no lo indica explícitamente. Sin embargo, es una característica importante de las transmisiones y trato de aclararla:
(first, second) -> second
.La documentación para los coleccionistas estrechamente relacionados es aún más explícita: "Para garantizar que las ejecuciones secuenciales y paralelas produzcan resultados equivalentes , las funciones del recopilador deben satisfacer restricciones de identidad y asociatividad ".
Volviendo a la pregunta original: el siguiente código almacena una referencia al último elemento de la variable
last
y genera una excepción si la secuencia está vacía. La complejidad es lineal en la longitud de la corriente.fuente
_
o similar) en los casos en que no necesita un parámetro? Así sería:.reduce((_, current) -> current)
si solo eso permitiera una sintaxis válida..reduce(($, current) -> current)
o.reduce((__, current) -> current)
(doble subrayado).Stream.reduce(BinaryOperator<T>)
tampoco menciona sireduce
obedece al orden de encuentro, y una operación de terminal es libre de ignorar el orden de encuentro incluso si el flujo está ordenado. Como acotación al margen, la palabra "conmutativa" no aparece en los javadocs Stream, por lo que su ausencia no nos dice mucho.reduce((a,b)->b)
ser una solución correcta para obtener el último elemento (de un flujo ordenado, por supuesto) o no. La declaración de Brian Goetz hace un punto, además la documentación de la API indica quereduce("", String::concat)
es una solución ineficiente pero correcta para la concatenación de cadenas, lo que implica el mantenimiento del orden de encuentro. La intención es bien conocida, la documentación debe ponerse al día.Si tienes una colección (o más general un iterable) puedes usar Google Guava's
como un delineador a mano.
fuente
Iterables.getLast(() -> data.careas.stream().filter(c -> c.bbox.orientationHorizontal).iterator())
Un trazador de líneas (sin necesidad de flujo;):
fuente
ArrayIndexOutOfBoundsException
.Guava tiene un método dedicado para este caso:
Es equivalente a
stream.reduce((a, b) -> b)
pero los creadores afirman que tiene un rendimiento mucho mejor.De la documentación :
Vale la pena mencionar que si la transmisión no está ordenada, este método se comporta como
findAny()
.fuente
Si necesita obtener el último número N de elementos. Se puede utilizar el cierre. El siguiente código mantiene una cola externa de tamaño fijo hasta que la secuencia llega al final.
Otra opción podría ser utilizar la operación de reducción utilizando la identidad como cola.
fuente
También puede usar la función skip () como se muestra a continuación ...
es superfácil de usar.
fuente
ArrayIndexOutOfBoundsException