95

In Python, the enumerate function allows you to iterate over a sequence of (index, value) pairs. For example:

>>> numbers = ["zero", "one", "two"]
>>> for i, s in enumerate(numbers):
...     print i, s
... 
0 zero
1 one
2 two

Is there any way of doing this in Java?

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Richard Fearn
  • 25,073
  • 7
  • 56
  • 55

11 Answers11

81

For collections that implement the List interface, you can call the listIterator() method to get a ListIterator. The iterator has (amongst others) two methods - nextIndex(), to get the index; and next(), to get the value (like other iterators).

So a Java equivalent of the Python above might be:

import java.util.ListIterator;  
import java.util.List;

List<String> numbers = Arrays.asList("zero", "one", "two");
ListIterator<String> it = numbers.listIterator();
while (it.hasNext()) {
    System.out.println(it.nextIndex() + " " + it.next());
}

which, like the Python, outputs:

0 zero
1 one
2 two
Cloud Cho
  • 1,594
  • 19
  • 22
Richard Fearn
  • 25,073
  • 7
  • 56
  • 55
  • 6
    So `it.next()` has a sideeffect? Is it guaranteed to be safe to mix `it.nextIndex()` and `it.next()` in the same expression? – John La Rooy Aug 23 '11 at 21:43
  • 2
    Yes, it goes to the next element. See http://download.oracle.com/javase/6/docs/api/java/util/ListIterator.html for how a ListIterator works. – JB Nizet Aug 23 '11 at 21:55
  • 4
    As @JB Nizet says, yes, `next()` has the side effect of advancing the iterator one element. However the Java Language Specification guarantees that the operands to the `+` operator are evaluated left-to-right. See [section 15.7](http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#4779). – Richard Fearn Aug 24 '11 at 07:40
  • 8
    This is an *alternative*. `enumerate` works quite differently. python's `enumerate` would index an arbitrary sequence independently of it's internal index state. It yields a 'substitute' iterable sequence with (index, element) pairs as elements. It accepts a `start` parameter that adds an offset to the index - can be done on the loop but still. It works natively with the for-each like loops. – n611x007 Nov 05 '13 at 11:24
24

I find this to be the most similar to the python approach.

Usage

public static void main(String [] args) {
    List<String> strings = Arrays.asList("zero", "one", "two");
    for(EnumeratedItem<String> stringItem : ListUtils.enumerate(strings)) {
        System.out.println(stringItem.index + " " + stringItem.item);
    }
    System.out.println();
    for(EnumeratedItem<String> stringItem : ListUtils.enumerate(strings, 3)) {
        System.out.println(stringItem.index + " " + stringItem.item);
    }
}

Output

0 zero
1 one
2 two

3 zero
4 one
5 two

Features

  • Works on any iterable
  • Does not create an in-memory list copy (suitable for large lists)
  • Supports native for each syntax
  • Accepts a start parameter which can be added to the index

Implementation

import java.util.Iterator;

public class ListUtils {

    public static class EnumeratedItem<T> {
        public T item;
        public int index;

        private EnumeratedItem(T item, int index) {
            this.item = item;
            this.index = index;
        }
    }

    private static class ListEnumerator<T> implements Iterable<EnumeratedItem<T>> {

        private Iterable<T> target;
        private int start;

        public ListEnumerator(Iterable<T> target, int start) {
            this.target = target;
            this.start = start;
        }

        @Override
        public Iterator<EnumeratedItem<T>> iterator() {
            final Iterator<T> targetIterator = target.iterator();
            return new Iterator<EnumeratedItem<T>>() {

                int index = start;

                @Override
                public boolean hasNext() {
                    return targetIterator.hasNext();
                }

                @Override
                public EnumeratedItem<T> next() {
                    EnumeratedItem<T> nextIndexedItem = new EnumeratedItem<T>(targetIterator.next(), index);
                    index++;
                    return nextIndexedItem;
                }

            };
        }

    }

    public static <T> Iterable<EnumeratedItem<T>> enumerate(Iterable<T> iterable, int start) {
        return new ListEnumerator<T>(iterable, start);
    }

    public static <T> Iterable<EnumeratedItem<T>> enumerate(Iterable<T> iterable) {
        return enumerate(iterable, 0);
    }

}
Pace
  • 41,875
  • 13
  • 113
  • 156
  • 2
    I was just thinking about implementing this sort of thing. I think this is a great candidate for the standard library. – dantiston Apr 25 '16 at 22:46
11

Strictly speaking, no, as the enumerate() function in Python returns a list of tuples, and tuples do not exist in Java.

If however, all you're interested in is printing out an index and a value, then you can follow the suggestion from Richard Fearn & use nextIndex() and next() on an iterator.

Note as well that enumerate() can be defined using the more general zip() function (using Python syntax):

mylist = list("abcd")
zip(range(len(mylist)), mylist)

gives [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')]

If you define your own Tuple class (see Using Pairs or 2-tuples in Java as a starting point), then you could certainly easily write your own zip() function in Java to make use of it (using the Tuple class defined in the link):

public static <X,Y> List<Tuple<X,Y>> zip(List<X> list_a, List<Y> list_b) {
    Iterator<X> xiter = list_a.iterator();
    Iterator<Y> yiter = list_b.iterator();

    List<Tuple<X,Y>> result = new LinkedList<Tuple<X,Y>>();

    while (xiter.hasNext() && yiter.hasNext()) {
        result.add(new Tuple<X,Y>(xiter.next(), yiter.next()));
    }

    return result;
}

And once you have zip(), implementing enumerate() is trivial.

Edit: slow day at work, so to finish it off:

public static <X> List<Tuple<Integer,X>> enumerate (List<X> list_in) {
    List<Integer> nums = new ArrayList<Integer>(list_in.size());
    for (int x = 0; x < list_in.size(); x++) { 
        nums.add(Integer.valueOf(x));
    }

    return zip (nums, list_in);
}

Edit 2: as pointed out in the comments to this question, this is not entirely equivalent. While it produces the same values as Python's enumerate, it doesn't do so in the same generative fashion that Python's enumerate does. Thus for large collections this approach could be quite prohibitive.

Community
  • 1
  • 1
Adam Parkin
  • 17,891
  • 17
  • 66
  • 87
  • I suppose technically as well that using an ArrayList initialized to the Math.min of the two input list lengths would be a better choice for the returned list, but the idea's the same. – Adam Parkin Aug 24 '11 at 17:20
  • 1
    However, it is inaccurate: python's `enumerate` does **not** return a list of tuples. it returns an 'enumerate object' that is [iterable](http://docs.python.org/2/tutorial/classes.html#iterators) since `enumerate` was [designed](http://www.python.org/dev/peps/pep-0279/) to be a [generator](http://docs.python.org/2/tutorial/classes.html#generators). – n611x007 Nov 05 '13 at 11:44
  • `zip` and `range` creates a list which is memory-inefficient on very large lists. iterators, like enumerate, only deal with the current element and a function to generate the next one. In python 2.x, there is [itertools.izip](http://docs.python.org/2/library/itertools.html#itertools.izip) and [xrange](http://docs.python.org/2/library/functions.html#xrange) to more closely emulate `enumerate`. – n611x007 Nov 05 '13 at 11:46
  • @naxa: Fair enough, from an efficiency point-of-view they're not equivalent, but from a final output point-of-view they are. Will update the answer with this. – Adam Parkin Nov 06 '13 at 18:55
8

Simple and straightforward

public static <T> void enumerate(Iterable<T> iterable, java.util.function.ObjIntConsumer<T> consumer) {
    int i = 0;
    for(T object : iterable) {
        consumer.accept(object, i);
        i++;
    }
}

Sample usage:

void testEnumerate() {
    List<String> strings = Arrays.asList("foo", "bar", "baz");
    enumerate(strings, (str, i) -> {
        System.out.println(String.format("Index:%d String:%s", i, str));
    });
}
balki
  • 26,394
  • 30
  • 105
  • 151
5

According to the Python docs (here), this is the closest you can get with Java, and it's no more verbose:

String[] numbers = {"zero", "one", "two"}
for (int i = 0; i < numbers.length; i++) // Note that length is a property of an array, not a function (hence the lack of () )
    System.out.println(i + " " + numbers[i]);
}

If you need to use the List class...

List<String> numbers = Arrays.asList("zero", "one", "two");
for (int i = 0; i < numbers.size(); i++) {
    System.out.println(i + " " + numbers.get(i));
}

*NOTE: if you need to modify the list as you're traversing it, you'll need to use the Iterator object, as it has the ability to modify the list without raising a ConcurrentModificationException.

Travis
  • 3,737
  • 1
  • 24
  • 24
3

Now with Java 8s Stream API together with the small ProtonPack library providing StreamUtils it can be achieved easily.

The first example uses the same for-each notation as in the question:

Stream<String> numbers = Arrays.stream("zero one two".split(" "));
List<Indexed<String>> indexedNumbers = StreamUtils.zipWithIndex(numbers)
                                                  .collect(Collectors.toList());
for (Indexed<String> indexed : indexedNumbers) {
    System.out.println(indexed.getIndex() + " " + indexed.getValue());
}

Above although does not provide the lazy evaluation as in Python. For that you must use the forEach() Stream API method:

Stream<String> numbers = Arrays.stream("zero one two".split(" "));
StreamUtils.zipWithIndex(numbers)
        .forEach(n -> System.out.println(n.getIndex() + " " + n.getValue()));

The lazy evaluation can be verified with the following infinite stream:

Stream<Integer> infStream = Stream.iterate(0, i -> i++);
StreamUtils.zipWithIndex(infStream)
        .limit(196)
        .forEach(n -> System.out.println(n.getIndex() + " " + n.getValue()));
Cwt
  • 8,206
  • 3
  • 32
  • 27
2

No. Maybe there are some libraries for supporting such a functionality. But if you resort to the standard libraries it is your job to count.

rocksportrocker
  • 7,251
  • 2
  • 31
  • 48
2
List<String> list = { "foo", "bar", "foobar"};
int i = 0;
for (String str : list){
     System.out.println(i++ + str );
}
Heisenbug
  • 38,762
  • 28
  • 132
  • 190
  • An i++ is missing at the end of the loop. And the syntax to initialize the list is not valid. You must use Arrays.asList(...) – JB Nizet Aug 23 '11 at 20:45
  • 1
    @JB Nizet: yes..thanks. I was editing it. I think I can use i++ directly inside the println because i should be incremented after its value has been returned – Heisenbug Aug 23 '11 at 20:46
  • 1
    IMHO having the `i++` in one of the statements is not a good choice, as it could cause hard-to-track bugs if that statement is (conditionally) skipped or executed more than once/copied. Better have a dedicated `i++` line. – tobias_k Jan 07 '19 at 16:37
1

I think this should be the java functionality that resemble the python "enumerate" most, though it is quite complicated and inefficent. Basically, just map the list's indices to its elements, using ListIterator or Collector:

List<String> list = new LinkedList<>(Arrays.asList("one", "two", "three", "four"));
Map<Integer, String> enumeration = new Map<>();
ListIterator iter = list.listIterator();
while(iter.hasNext){
    map.put(iter.nextIndex(), iter.next());
}

or using lambda expression:

Set<Integer, String> enumeration = IntStream.range(0, list.size()).boxed.collect(Collectors.toMap(index -> index, index -> list.get(index)));

then you can use it with an enhanced for loop:

for (Map.Entry<Integer, String> entry : enumeration.entrySet){
    System.out.println(entry.getKey() + "\t" + entry.getValue());
}
charlieh_7
  • 334
  • 3
  • 12
1

By combining generics with anonymous interfaces, you can essentially create a factory method for handing enumeration. The Enumerator callback hides the messiness of the iterator underneath.

import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;

public class ListUtils2 {
    public static interface Enumerator<T> {
        void execute(int index, T value);
    };

    public static final <T> void enumerate(final List<T> list,
            final Enumerator<T> enumerator) {
        for (ListIterator<T> it = list.listIterator(); it.hasNext();) {
            enumerator.execute(it.nextIndex(), it.next());
        }
    }

    public static final void enumerate(final String[] arr,
            final Enumerator<String> enumerator) {
        enumerate(Arrays.asList(arr), enumerator);
    }

    public static void main(String[] args) {
        String[] names = { "John", "Paul", "George", "Ringo" };

        enumerate(names, new Enumerator<String>() {
            @Override
            public void execute(int index, String value) {
                System.out.printf("[%d] %s%n", index, value);
            }
        });
    }
}

Result

[0] John
[1] Paul
[2] George
[3] Ringo

Extended Thoughts

Map, Reduce, Filter

I have taken this a step further and created map, reduce, and filter functions based on this concept.

Both Google's Guava and Apache common-collections dependencies include similar functionality. You can check them out as you wish.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;

public class ListUtils {
    // =========================================================================
    // Enumerate
    // =========================================================================
    public static abstract interface Enumerator<T> {
        void execute(int index, T value, List<T> list);
    };

    public static final <T> void enumerate(final List<T> list,
            final Enumerator<T> enumerator) {
        for (ListIterator<T> it = list.listIterator(); it.hasNext();) {
            enumerator.execute(it.nextIndex(), it.next(), list);
        }
    }

    // =========================================================================
    // Map
    // =========================================================================
    public static interface Transformer<T, U> {
        U execute(int index, T value, List<T> list);
    };

    public static final <T, U> List<U> transform(final List<T> list,
            final Transformer<T, U> transformer) {
        List<U> result = new ArrayList<U>();
        for (ListIterator<T> it = list.listIterator(); it.hasNext();) {
            result.add(transformer.execute(it.nextIndex(), it.next(), list));
        }
        return result;
    }

    // =========================================================================
    // Reduce
    // =========================================================================
    public static interface Reducer<T, U> {
        U execute(int index, T value, U result, List<T> list);
    };

    public static final <T, U> U reduce(final List<T> list,
            final Reducer<T, U> enumerator, U result) {
        for (ListIterator<T> it = list.listIterator(); it.hasNext();) {
            result = enumerator.execute(it.nextIndex(), it.next(), result, list);
        }
        return result;
    }

    // =========================================================================
    // Filter
    // =========================================================================
    public static interface Predicate<T> {
        boolean execute(int index, T value, List<T> list);
    };

    public static final <T> List<T> filter(final List<T> list,
            final Predicate<T> predicate) {
        List<T> result = new ArrayList<T>();
        for (ListIterator<T> it = list.listIterator(); it.hasNext();) {
            int index = it.nextIndex();
            T value = it.next();
            if (predicate.execute(index, value, list)) {
                result.add(value);
            }
        }
        return result;
    }

    // =========================================================================
    // Predefined Methods
    // =========================================================================
    // Enumerate
    public static <T> String printTuples(List<T> list) {
        StringBuffer buff = new StringBuffer();

        enumerate(list, new Enumerator<T>() {
            @Override
            public void execute(int index, T value, List<T> list) {
                buff.append('(').append(index).append(", ")
                    .append(value).append(')');
                if (index < list.size() - 1) {
                    buff.append(", ");
                }
            }
        });

        return buff.toString();
    }

    // Map
    public static List<String> intToHex(List<Integer> list) {
        return transform(list, new Transformer<Integer, String>() {
            @Override
            public String execute(int index, Integer value, List<Integer> list) {
                return String.format("0x%02X", value);
            }
        });
    }

    // Reduce
    public static Integer sum(List<Integer> list) {
        return reduce(list, new Reducer<Integer, Integer>() {
            @Override
            public Integer execute(int index, Integer value, Integer result,
                    List<Integer> list) {
                return result + value;
            }
        }, 0);
    }

    // Filter
    public static List<Integer> evenNumbers(List<Integer> list) {
        return filter(list, new Predicate<Integer>() {
            @Override
            public boolean execute(int index, Integer value, List<Integer> list) {
                return value % 2 == 0;
            }
        });
    }

    // =========================================================================
    // Driver
    // =========================================================================
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(8, 6, 7, 5, 3, 0, 9);

        // Enumerate
        System.out.printf("%-10s: %s%n", "Enumerate", printTuples(numbers));

        // Map
        System.out.printf("%-10s: %s%n", "Map", intToHex(numbers));

        // Reduce
        System.out.printf("%-10s: %d%n", "Reduce", sum(numbers));

        // Filter
        System.out.printf("%-10s: %s%n", "Filter", evenNumbers(numbers));
    }
}
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
-3

Pretty much the same syntax using Java8 Streams

    ArrayList<String> numbers = new ArrayList<String>();
    numbers.add("one");
    numbers.add("two");
    numbers.add("three");

    numbers.stream().forEach(num ->
    {
        System.out.println(numbers.indexOf(num) + " " + num);
    });
  • 1
    Not really; this requires traversing the list on each iteration to find the index of the current element, and won't work if items are duplicated in the list. – Richard Fearn Feb 10 '20 at 12:46