Question

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.

Était-ce utile?

La solution

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));

Autres conseils

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")));
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top