Pregunta

I wonder, if there's some stream operation that can do as each_with_index in ruby.

Where each_with_index iterates over the value as well as the index of the value.

¿Fue útil?

Solución

There is no stream operation specifically for that purpose. But you can mimic the functionality in several ways.

Index variable: The following approach works fine for sequential streams.

int[] index = { 0 };
stream.forEach(item -> System.out.printf("%s %d\n", item, index[0]++));

External iteration: The following approach works fine for parallel streams, as long as the original collection supports random access.

List<String> tokens = ...;
IntStream.range(0, tokens.size()).forEach(
    index -> System.out.printf("%s %d\n", tokens.get(index), index));

Otros consejos

You can reduce it

<T> void forEachIndexed(Stream<T> stream, BiConsumer<Integer, T> consumer) {
    stream.reduce(0, (index, t) -> {
        consumer.accept(index, t);
        return index + 1;
    }, Integer::max);
}

this way:

List<Integer> ints = Arrays.asList(1, 2, 4, 6, 8, 16, 32);

forEachIndexed(ints.stream(), (idx, el) -> {
     System.out.println(idx + ": " + el);
});

You can use forEachWithIndex() in Eclipse Collections (formerly GS Collections).

MutableList<Integer> elements = FastList.newList();
IntArrayList indexes = new IntArrayList();
MutableList<Integer> collection = this.newWith(1, 2, 3, 4);
collection.forEachWithIndex((Integer object, int index) -> {
    elements.add(object);
    indexes.add(index);
});
Assert.assertEquals(FastList.newListWith(1, 2, 3, 4), elements);
Assert.assertEquals(IntArrayList.newListWith(0, 1, 2, 3), indexes);

If you cannot convert your Collection to a GS Collections type, you can use one of the adapters, like ListAdapter.

List<Integer> list = Arrays.asList(1, 2, 3, 4);
ListIterable<Integer> collection = ListAdapter.adapt(list);

collection.forEachWithIndex((object, index) -> {
    elements.add(object);
    indexes.add(index);
});

Note: I am a committer for Eclipse Collections.

Alternative with stream reduce operation with accumulator (2nd parameter) for side-effect. 3rd parameter could be any function, if you don't need the result from reduce operation.

 List<String> tokens = Arrays.asList("A", "B", "C", "D");
 tokens.stream().reduce(1, (i, str) -> {
        System.out.printf("%s %d\n", str, i);
        return i + 1;
    }, Integer::max);

PS: Although it is possible, I am personally not satisfied with abuse of reduce function. :)

Easy to do with utility library protonpack: https://github.com/poetix/protonpack

Stream<String> source = Stream.of("Foo", "Bar", "Baz");
List<Indexed<String>> zipped = StreamUtils.zipWithIndex(source).collect(Collectors.toList());
assertThat(zipped, contains(
    Indexed.index(0, "Foo"),
    Indexed.index(1, "Bar"),
    Indexed.index(2, "Baz")));
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top