una recorrer listas lista iterar index for expressions con comparar java java-8 java-stream

recorrer - ¿Hay una forma concisa de iterar sobre una secuencia con índices en Java 8?



recorrer lista con lambda (17)

¿Hay una forma concisa de iterar sobre una transmisión mientras se tiene acceso al índice en la transmisión?

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 de LINQ dado allí

string[] names = { "Sam", "Pamela", "Dave", "Pascal", "Erik" }; var nameList = names.Where((c, index) => c.Length <= index + 1).ToList();

¿Hay alguna forma más concisa?

Además, parece que la cremallera se ha movido o se ha eliminado ...


Además de protonpack, el Seq de jOOλ proporciona esta funcionalidad (y por las bibliotecas de extensión que se basan en él como cyclops-react , soy el autor de esta biblioteca).

Seq.seq(Stream.of(names)).zipWithIndex() .filter( namesWithIndex -> namesWithIndex.v1.length() <= namesWithIndex.v2 + 1) .toList();

Seq también admite solo Seq.of (nombres) y creará un JDK Stream bajo las carátulas.

El equivalente de reacción simple se vería similarmente

LazyFutureStream.of(names) .zipWithIndex() .filter( namesWithIndex -> namesWithIndex.v1.length() <= namesWithIndex.v2 + 1) .toList();

La versión de reacción simple está más adaptada para el procesamiento asíncrono / concurrente.


Aquí está el código de AbacusUtil.

Stream.of(names).indexed() .filter(e -> e.value().length() <= e.index()) .map(Indexed::value).toList();

Divulgación: Soy el desarrollador de AbacusUtil.


Con https://github.com/poetix/protonpack u puede hacer ese zip:

String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"}; List<String> nameList; Stream<Integer> indices = IntStream.range(0, names.length).boxed(); nameList = StreamUtils.zip(indices, stream(names),SimpleEntry::new) .filter(e -> e.getValue().length() <= e.getKey()).map(Entry::getValue).collect(toList()); System.out.println(nameList);


Con una lista puedes probar

List<String> strings = new ArrayList<>(Arrays.asList("First", "Second", "Third", "Fourth", "Fifth")); // An example list of Strings strings.stream() // Turn the list into a Stream .collect(HashMap::new, (h, o) -> h.put(h.size(), o), (h, o) -> {}) // Create a map of the index to the object .forEach((i, o) -> { // Now we can use a BiConsumer forEach! System.out.println(String.format("%d => %s", i, o)); });

Salida:

0 => First 1 => Second 2 => Third 3 => Fourth 4 => Fifth


Desde guava 21, puedes usar

Streams.mapWithIndex()

Ejemplo (del documento oficial ):

Streams.mapWithIndex( Stream.of("a", "b", "c"), (str, index) -> str + ":" + index) ) // will return Stream.of("a:0", "b:1", "c:2")


Encontré las soluciones aquí cuando se crea la secuencia de lista o matriz (y usted sabe el tamaño). ¿Pero qué pasa si Stream tiene un tamaño desconocido? En este caso prueba esta variante:

public class WithIndex<T> { private int index; private T value; WithIndex(int index, T value) { this.index = index; this.value = value; } public int index() { return index; } public T value() { return value; } @Override public String toString() { return value + "(" + index + ")"; } public static <T> Function<T, WithIndex<T>> indexed() { return new Function<T, WithIndex<T>>() { int index = 0; @Override public WithIndex<T> apply(T t) { return new WithIndex<>(index++, t); } }; } }

Uso:

public static void main(String[] args) { Stream<String> stream = Stream.of("a", "b", "c", "d", "e"); stream.map(WithIndex.indexed()).forEachOrdered(e -> { System.out.println(e.index() + " -> " + e.value()); }); }


Esta pregunta ( Stream Way para obtener el índice del primer elemento booleano ) ha marcado la pregunta actual como un duplicado, por lo que no puedo responderla allí; 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.

public static <T> int indexOf(List<T> items, Predicate<T> matches) { return IntStream.range(0, items.size()) .filter(index -> matches.test(items.get(index))) .findFirst().orElse(-1); }

Y llámalo así:

int index = indexOf(myList, item->item.getId()==100);

Y si usas una colección, prueba esta.

public static <T> int indexOf(Collection<T> items, Predicate<T> matches) { int index = -1; Iterator<T> it = items.iterator(); while (it.hasNext()) { index++; if (matches.test(it.next())) { return index; } } return -1; }


He usado la siguiente solución en mi proyecto. Creo que es mejor que usar objetos mutables o rangos enteros.

import java.util.*; import java.util.function.*; import java.util.stream.Collector; import java.util.stream.Collector.Characteristics; import java.util.stream.Stream; import java.util.stream.StreamSupport; import static java.util.Objects.requireNonNull; public class CollectionUtils { private CollectionUtils() { } /** * Converts an {@link java.util.Iterator} to {@link java.util.stream.Stream}. */ public static <T> Stream<T> iterate(Iterator<? extends T> iterator) { int characteristics = Spliterator.ORDERED | Spliterator.IMMUTABLE; return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, characteristics), false); } /** * Zips the specified stream with its indices. */ public static <T> Stream<Map.Entry<Integer, T>> zipWithIndex(Stream<? extends T> stream) { return iterate(new Iterator<Map.Entry<Integer, T>>() { private final Iterator<? extends T> streamIterator = stream.iterator(); private int index = 0; @Override public boolean hasNext() { return streamIterator.hasNext(); } @Override public Map.Entry<Integer, T> next() { return new AbstractMap.SimpleImmutableEntry<>(index++, streamIterator.next()); } }); } /** * Returns a stream consisting of the results of applying the given two-arguments function to the elements of this stream. * The first argument of the function is the element index and the second one - the element value. */ public static <T, R> Stream<R> mapWithIndex(Stream<? extends T> stream, BiFunction<Integer, ? super T, ? extends R> mapper) { return zipWithIndex(stream).map(entry -> mapper.apply(entry.getKey(), entry.getValue())); } public static void main(String[] args) { String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"}; System.out.println("Test zipWithIndex"); zipWithIndex(Arrays.stream(names)).forEach(entry -> System.out.println(entry)); System.out.println(); System.out.println("Test mapWithIndex"); mapWithIndex(Arrays.stream(names), (Integer index, String name) -> index+"="+name).forEach((String s) -> System.out.println(s)); } }


La API de flujos de Java 8 carece de las características de obtener el índice de un elemento de flujo, así como la capacidad de comprimir los flujos juntos. Esto es desafortunado, ya que hace que algunas 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" el flujo 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 a la que se puede acceder mediante un índice. Por ejemplo, el problema del desafío 2 se puede resolver de esta manera:

String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"}; List<String> nameList = IntStream.range(0, names.length) .filter(i -> names[i].length() <= i) .mapToObj(i -> names[i]) .collect(toList());

Como mencioné anteriormente, esto se aprovecha del 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. No obstante, resuelve el problema de manera razonablemente efectiva.

EDITAR

Mi ejemplo de código anterior usaba flatMap para fusionar las operaciones de filtro y mapa, pero esto era engorroso y no ofrecía ninguna ventaja. He actualizado el ejemplo por el comentario de Holger.


La forma más limpia es comenzar desde un flujo de índices:

String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"}; IntStream.range(0, names.length) .filter(i -> names[i].length() <= i) .mapToObj(i -> names[i]) .collect(Collectors.toList());

La lista resultante contiene "Erik" solamente.

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 :

String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"}; AtomicInteger index = new AtomicInteger(); List<String> list = Arrays.stream(names) .filter(n -> n.length() <= index.incrementAndGet()) .collect(Collectors.toList());

Tenga en cuenta que el uso de este último método en una secuencia paralela podría interrumpirse ya que los elementos no serían procesados ​​"en orden" .


No hay una manera de iterar sobre una Stream mientras se tiene acceso al índice porque una Stream es diferente a cualquier Collection . Un Stream es simplemente un conducto para transportar datos de un lugar a otro, como se indica en la documentation :

Sin almacenamiento. Un flujo no es una estructura de datos que almacena elementos; en su lugar, transportan valores desde una fuente (que podría ser una estructura de datos, un generador, un canal de 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> a una Collection<V> , como una List<V> , en la que tendrá acceso a los índices.


Puede crear una clase interna estática para encapsular el indexador como necesitaba hacer en el siguiente ejemplo:

static class Indexer { int i = 0; } public static String getRegex() { EnumSet<MeasureUnit> range = EnumSet.allOf(MeasureUnit.class); StringBuilder sb = new StringBuilder(); Indexer indexer = new Indexer(); range.stream().forEach( measureUnit -> { sb.append(measureUnit.acronym); if (indexer.i < range.size() - 1) sb.append("|"); indexer.i++; } ); return sb.toString(); }


Si está intentando obtener un índice basado en un predicado, intente esto:

Si solo te importa el primer índice:

OptionalInt index = IntStream.range(0, list.size()) .filter(i -> list.get(i) == 3) .findFirst();

O si quieres encontrar múltiples índices:

IntStream.range(0, list.size()) .filter(i -> list.get(i) == 3) .collect(Collectors.toList());

Añadir .orElse(-1); En caso de que quiera devolver un valor si no lo encuentra.


Si no le importa usar una biblioteca de terceros, Eclipse Collections tiene zipWithIndex y forEachWithIndex disponibles para su uso en muchos tipos. Aquí hay un conjunto de soluciones a este desafío para los tipos JDK y los tipos de colecciones Eclipse usando zipWithIndex .

String[] names = { "Sam", "Pamela", "Dave", "Pascal", "Erik" }; ImmutableList<String> expected = Lists.immutable.with("Erik"); Predicate<Pair<String, Integer>> predicate = pair -> pair.getOne().length() <= pair.getTwo() + 1; // JDK Types List<String> strings1 = ArrayIterate.zipWithIndex(names) .collectIf(predicate, Pair::getOne); Assert.assertEquals(expected, strings1); List<String> list = Arrays.asList(names); List<String> strings2 = ListAdapter.adapt(list) .zipWithIndex() .collectIf(predicate, Pair::getOne); Assert.assertEquals(expected, strings2); // Eclipse Collections types MutableList<String> mutableNames = Lists.mutable.with(names); MutableList<String> strings3 = mutableNames.zipWithIndex() .collectIf(predicate, Pair::getOne); Assert.assertEquals(expected, strings3); ImmutableList<String> immutableNames = Lists.immutable.with(names); ImmutableList<String> strings4 = immutableNames.zipWithIndex() .collectIf(predicate, Pair::getOne); Assert.assertEquals(expected, strings4); MutableList<String> strings5 = mutableNames.asLazy() .zipWithIndex() .collectIf(predicate, Pair::getOne, Lists.mutable.empty()); Assert.assertEquals(expected, strings5);

Aquí hay una solución usando forEachWithIndex en forEachWithIndex lugar.

MutableList<String> mutableNames = Lists.mutable.with("Sam", "Pamela", "Dave", "Pascal", "Erik"); ImmutableList<String> expected = Lists.immutable.with("Erik"); List<String> actual = Lists.mutable.empty(); mutableNames.forEachWithIndex((name, index) -> { if (name.length() <= index + 1) actual.add(name); }); Assert.assertEquals(expected, actual);

Si cambia las lambdas a clases internas anónimas de arriba, entonces todos estos ejemplos de código también funcionarán en Java 5 - 7.

Nota: Soy un comendador de Eclipse Collections.


Si usas Vavr (anteriormente conocido como Javaslang), puedes aprovechar el método dedicado:

Stream.of("A", "B", "C") .zipWithIndex();

Si imprimimos el contenido, veremos algo interesante:

Stream((A, 0), ?)

Esto se debe a que los Streams son perezosos y no tenemos idea de los próximos elementos en el stream.


Solo para completar, aquí está la solución que involucra mi biblioteca StreamEx :

String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"}; EntryStream.of(names) .filterKeyValue((idx, str) -> str.length() <= idx+1) .values().toList();

Aquí creamos un EntryStream<Integer, String> que extiende el Stream<Entry<Integer, String>> y agrega algunas operaciones específicas como filterKeyValue o values . También se toList() método abreviado toList() .


Una forma posible es indexar cada elemento en el flujo:

AtomicInteger index = new AtomicInteger(); Stream.of(names) .map(e->new Object() { String n=e; public i=index.getAndIncrement(); }) .filter(o->o.n.length()<=o.i) // or do whatever you want with pairs... .forEach(o->System.out.println("idx:"+o.i+" nam:"+o.n));

El uso de una clase anónima a lo largo de una secuencia no se usa bien y es muy útil