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

JavaPythonIterator

Java Problem Overview


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?

Java Solutions


Solution 1 - Java

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

Solution 2 - Java

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

Solution 3 - Java

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 https://stackoverflow.com/questions/2670982/using-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.

Solution 4 - Java

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

Solution 5 - Java

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.

Solution 6 - Java

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

Solution 7 - Java

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

Solution 8 - Java

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.

Solution 9 - Java

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

Solution 10 - Java

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

Solution 11 - Java

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRichard FearnView Question on Stackoverflow
Solution 1 - JavaRichard FearnView Answer on Stackoverflow
Solution 2 - JavaPaceView Answer on Stackoverflow
Solution 3 - JavaAdam ParkinView Answer on Stackoverflow
Solution 4 - JavabalkiView Answer on Stackoverflow
Solution 5 - JavaTravisView Answer on Stackoverflow
Solution 6 - JavasevenforceView Answer on Stackoverflow
Solution 7 - JavaHeisenbugView Answer on Stackoverflow
Solution 8 - JavarocksportrockerView Answer on Stackoverflow
Solution 9 - Javacharlieh_7View Answer on Stackoverflow
Solution 10 - JavaMr. PolywhirlView Answer on Stackoverflow
Solution 11 - JavaDavid Carracedo MartínezView Answer on Stackoverflow