Home » Java » Is there a Java equivalent of Python's 'enumerate' function?

Is there a Java equivalent of Python's 'enumerate' function?

Posted by: admin December 20, 2017 Leave a comment

Questions:

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?

Answers:

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:

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

Questions:
Answers:

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.

Questions:
Answers:

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

}

Questions:
Answers:

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.

Questions:
Answers:
List<String> list = { "foo", "bar", "foobar"};
int i = 0;
for (String str : list){
     System.out.println(i++ + str );
}

Questions:
Answers:

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.

Questions:
Answers:

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

Questions:
Answers:

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

Questions:
Answers:

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

Questions:
Answers:

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