¿Hay una manera concisa de iterar sobre una secuencia mientras se tiene acceso al índice en la secuencia?
String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
List<String> nameList;
Stream<Integer> indices = intRange(1, names.length).boxed();
nameList = zip(indices, stream(names), SimpleEntry::new)
.filter(e -> e.getValue().length() <= e.getKey())
.map(Entry::getValue)
.collect(toList());
lo que parece bastante decepcionante en comparación con el ejemplo LINQ dado allí
string[] names = { "Sam", "Pamela", "Dave", "Pascal", "Erik" };
var nameList = names.Where((c, index) => c.Length <= index + 1).ToList();
¿Hay una manera más concisa?
Además, parece que la cremallera se ha movido o se ha eliminado ...
java
java-8
java-stream
Graeme Moss
fuente
fuente
intRange()
? No he encontrado este método en Java 8 hasta ahora.IntStream.rangeClosed(x, y)
.List<String> allCities = map.values().stream().flatMap(list -> list.stream()).collect(Collectors.toList());
zip
se eliminó, junto con secuencias experimentales de dos valores denominadas de forma diferenteBiStream
oMapStream
. El principal problema es que para hacer esto efectivamente, Java realmente necesita un tipo de par (o tupla) estructuralmente tipado. A falta de uno, es fácil crear una clase genérica de pares o tuplas, se ha hecho muchas veces, pero todas se borran del mismo tipo.Respuestas:
La forma más limpia es comenzar desde una secuencia de índices:
La lista resultante contiene solo "Erik".
Una alternativa que parece más familiar cuando está acostumbrado a los bucles sería mantener un contador ad hoc utilizando un objeto mutable, por ejemplo un
AtomicInteger
:Tenga en cuenta que el uso del último método en una secuencia paralela podría romperse ya que los elementos no se procesarían necesariamente "en orden" .
fuente
public static <T> Stream<Tuple2<Integer, T>> zipWithIndex(Stream<T> stream) { final AtomicInteger index = new AtomicInteger(); final Function<T, Tuple2<Integer, T>> zipper = e -> Tuples.of(index.getAndIncrement(), e); if (stream.isParallel()) { return stream.sequential().map(zipper).parallel(); } else { return stream.map(zipper); } }
parallel
o el últimosequential
cuando comienza la operación del terminal.La API de secuencias de Java 8 carece de las características para obtener el índice de un elemento de secuencia, así como la capacidad de agrupar secuencias. Esto es lamentable, ya que hace que ciertas aplicaciones (como los desafíos de LINQ) sean más difíciles de lo que serían de otra manera.
Sin embargo, a menudo hay soluciones alternativas. Por lo general, esto se puede hacer "conduciendo" la secuencia con un rango entero y aprovechando el hecho de que los elementos originales a menudo están en una matriz o en una colección accesible por índice. Por ejemplo, el problema del Desafío 2 se puede resolver de esta manera:
Como mencioné anteriormente, esto aprovecha el hecho de que la fuente de datos (la matriz de nombres) es directamente indexable. Si no fuera así, esta técnica no funcionaría.
Admito que esto no satisface la intención del Desafío 2. Sin embargo, resuelve el problema de manera razonablemente efectiva.
EDITAR
Mi ejemplo de código anterior solía
flatMap
fusionar las operaciones de filtro y mapa, pero esto era engorroso y no proporcionaba ninguna ventaja. He actualizado el ejemplo según el comentario de Holger.fuente
IntStream.range(0, names.length).filter(i->names[i].length()<=i).mapToObj(i->names[i])
? Funciona sin boxeo ...flatMap
todos modos?flatMap
porque fusiona una operación de filtrado y mapeo en una sola operación, pero esto realmente no proporciona ninguna ventaja. Editaré el ejemplo.Stream.of( names ).filter( n -> n.length() <= 1).collect( Collectors.toList() );
Menos unboxing y menos asignación de memoria; ya que ya no estamos creando una secuencia de rango.Desde guayaba 21, puedes usar
Ejemplo (del documento oficial ):
fuente
He usado la siguiente solución en mi proyecto. Creo que es mejor que usar objetos mutables o rangos de enteros.
fuente
StreamSupport.stream()
y un iterador personalizado.Además de protonpack, la Seq de jOOλ proporciona esta funcionalidad (y por bibliotecas de extensión que se basan en ella como cyclops-react , yo soy el autor de esta biblioteca).
Seq también admite solo Seq.of (nombres) y creará una secuencia JDK debajo de las cubiertas.
El equivalente de reacción simple sería similar a
La versión de reacción simple está más adaptada para el procesamiento asíncrono / concurrente.
fuente
Para completar, aquí está la solución que involucra mi biblioteca StreamEx :
Aquí creamos un
EntryStream<Integer, String>
que extiendeStream<Entry<Integer, String>>
y agrega algunas operaciones específicas comofilterKeyValue
ovalues
. TambiéntoList()
se usa el atajo.fuente
.forEach(entry -> {})
?.forKeyValue((key, value) -> {})
.Encontré las soluciones aquí cuando el Stream se crea de una lista o matriz (y sabes el tamaño). Pero, ¿qué pasa si Stream tiene un tamaño desconocido? En este caso, pruebe esta variante:
Uso:
fuente
Con una Lista puedes probar
Salida:
fuente
No hay una forma de iterar durante un
Stream
tiempo teniendo acceso al índice porque aStream
es diferente a cualquieraCollection
. AStream
es simplemente una tubería para transportar datos de un lugar a otro, como se indica en la documentación :Sin almacenamiento. Una secuencia no es una estructura de datos que almacena elementos; en cambio, transportan valores de una fuente (que podría ser una estructura de datos, un generador, un canal IO, etc.) a través de una tubería de operaciones computacionales.
Por supuesto, como parece estar insinuando en su pregunta, siempre puede convertir su
Stream<V>
en unCollection<V>
, como aList<V>
, en el que tendrá acceso a los índices.fuente
Con https://github.com/poetix/protonpack puedes hacer eso zip:
fuente
Si no le importa usar una biblioteca de terceros, Eclipse Collections tiene
zipWithIndex
y estáforEachWithIndex
disponible para su uso en muchos tipos. Aquí hay un conjunto de soluciones a este desafío para los tipos JDK y Eclipse CollectionszipWithIndex
.Aquí hay una solución usando en su
forEachWithIndex
lugar.Si cambia las lambdas a las clases internas anónimas anteriores, todos estos ejemplos de código funcionarán también en Java 5 - 7.
Nota: Soy un committer para Eclipse Collections
fuente
Si utiliza Vavr (anteriormente conocido como Javaslang), puede aprovechar el método dedicado:
Si imprimimos el contenido, veremos algo interesante:
Esto se debe a que
Streams
son flojos y no tenemos idea de los próximos elementos en la transmisión.fuente
Si está intentando obtener un índice basado en un predicado, intente esto:
Si solo te importa el primer índice:
O si quieres encontrar múltiples índices:
Agregue
.orElse(-1);
en caso de que desee devolver un valor si no lo encuentra.fuente
Aquí está el código de AbacusUtil
Divulgación: Soy el desarrollador de AbacusUtil.
fuente
Puede usar
IntStream.iterate()
para obtener el índice:Esto solo funciona para Java 9 hacia arriba en Java 8, puede usar esto:
fuente
Puede crear una clase interna estática para encapsular el indexador como necesitaba hacer en el siguiente ejemplo:
fuente
Esta pregunta ( Stream Way para obtener el índice del primer elemento que coincide con el booleano ) ha marcado la pregunta actual como un duplicado, por lo que no puedo responderla allí; Lo estoy respondiendo aquí.
Aquí hay una solución genérica para obtener el índice coincidente que no requiere una biblioteca externa.
Si tienes una lista.
Y llámalo así:
Y si usa una colección, pruebe esta.
fuente
Una forma posible es indexar cada elemento en el flujo:
El uso de una clase anónima a lo largo de una secuencia no se usa bien mientras es muy útil.
fuente
no necesita
map
necesariamente unalambda más cercana al ejemplo de LINQ:
fuente
SALIDA: Sam, Pamela, Dave, Pascal, Erik
Para recoger en la lista:
fuente
List
elemento que contenga Erik .Como dijo jean-baptiste-yunès, si su transmisión se basa en una Lista de Java, entonces usar un AtomicInteger y su método incrementAndGet es una muy buena solución al problema y el entero devuelto corresponde al índice en la Lista original siempre que No use una corriente paralela.
fuente
Si necesita el índice en forEach, esto proporciona una manera.
Luego úsalo de la siguiente manera.
fuente