3 years ago a similar question was asked here: Is there a Java equivalent of Python's 'enumerate' function?
I really appreciate the listIterator()
solution. Still, I work a lot with the new streams and lambdas (introduced in JDK 8) nowadays and wonder: is there an elegant way of obtaining the index of the element being currently processed? Mine is presented below, but I do not find it especially appealing.
IntStream.range(0, myList.size())
.mapToObj(i -> doSthWith(myList.get(i), i));
This question has been asked a few ways before. The key observation is that, unless you have perfect size and splitting information (basically, if your source is an array), then this would be a sequential-only operation.
The "unappealing" answer you propose:
IntStream.range(0, myList.size())
.mapToObj(i -> doSthWith(myList.get(i), i));
is actually quite efficient when myList
is an ArrayList
or other list with a fast O(1) indexed-get operation, and parallelizes cleanly. So I think there's nothing wrong with it.
There can be some workarounds to this.
You can use an AtomicInteger
that you will increment each time you go through an element of the stream
final AtomicInteger atom = new AtomicInteger();
list.stream().forEach(i -> System.out.println(i+"-"+atom.getAndIncrement()));
Or using an iterator from another stream for the indexes (a bit like your original idea) but more efficient as you don't call get
on the list.
final Iterator<Integer> a = IntStream.range(0, list.size()).iterator();
list.stream().forEach(i -> System.out.println(i+"-"+a.next()));
Well I'm not sure is there exist other nicer alternatives to this (certainly) but that's what I think for the moment.
Note that it's assuming that you are not using a parallel stream. In the latter case that would not be possible to do it like that to obtain a mapping of the elements with their original indexes in the list.
If you want a solution that works with non-RandomAccess
lists as well, you can write yourself a simple utility method:
public static <T> void forEach(List<T> list, BiConsumer<Integer,? super T> c) {
Objects.requireNonNull(c);
if(list.isEmpty()) return;
if(list instanceof RandomAccess)
for(int i=0, num=list.size(); i<num; i++)
c.accept(i, list.get(i));
else
for(ListIterator<T> it=list.listIterator(); it.hasNext(); ) {
c.accept(it.nextIndex(), it.next());
}
}
Then you can use it like: forEach(list, (i,s)->System.out.println(i+"\t"+s));
If you swap the order of element and index, you can use an ObjIntConsumer
instead of BiConsumer<Integer,? super T>
to avoid potential boxing overhead:
public static <T> void forEach(List<T> list, ObjIntConsumer<? super T> c) {
Objects.requireNonNull(c);
if(list.isEmpty()) return;
if(list instanceof RandomAccess)
for(int i=0, num=list.size(); i<num; i++)
c.accept(list.get(i), i);
else
for(ListIterator<T> it=list.listIterator(); it.hasNext(); ) {
c.accept(it.next(), it.previousIndex());
}
}
Then, to be used like forEach(list, (s,i) -> System.out.println(i+"\t"+s));
…
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With