En Python, la fonction enumerate
vous permet de parcourir une séquence de paires (index, valeur). Par exemple:
>>> numbers = ["zero", "one", "two"]
>>> for i, s in enumerate(numbers):
... print i, s
...
0 zero
1 one
2 two
Y a-t-il un moyen de faire cela en Java?
Pour les collections qui implémentent l'interface List
, vous pouvez appeler la méthode listIterator()
pour obtenir un ListIterator
. L'itérateur a (entre autres) deux méthodes - nextIndex()
, pour obtenir l'index; et next()
, pour obtenir la valeur (comme les autres itérateurs).
Donc, un équivalent Java du Python ci-dessus pourrait être:
List<String> numbers = Arrays.asList("zero", "one", "two");
ListIterator<String> it = numbers.listIterator();
while (it.hasNext()) {
System.out.println(it.nextIndex() + " " + it.next());
}
qui, comme le Python, génère:
0 zero
1 one
2 two
Je trouve que cela ressemble le plus à l’approche python.
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);
}
}
0 zero
1 one
2 two
3 zero
4 one
5 two
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);
}
}
Strictement parlant, non, car la fonction enumerate () de Python renvoie une liste de tuples, et les tuples n’existent pas en Java.
Si toutefois, tout ce qui vous intéresse est de imprimer un index et une valeur, vous pouvez suivre la suggestion de Richard Fearn & utiliser nextIndex () et next () sur un itérateur.
Notez également qu'énumerate () peut être défini à l'aide de la fonction plus générale Zip () (à l'aide de la syntaxe Python):
mylist = list("abcd")
Zip(range(len(mylist)), mylist)
donne [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')]
Si vous définissez votre propre classe Tuple (voir Utiliser Pairs ou 2-tuples en Java comme point de départ), alors vous pourrez certainement facilement écrire votre propre fonction Zip () en Java pour l'utiliser (en utilisant le tuple classe définie dans le lien):
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;
}
Et une fois que vous avez Zip (), implémenter enumerate () est trivial.
Edit: journée au boulot lente, pour finir:
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: comme indiqué dans les commentaires à cette question, ce n'est pas tout à fait équivalent. Bien qu'il produise les mêmes valeurs que l'énumération de Python, il ne le fait pas de la même manière générative que l'énumération de Python. Ainsi, pour les grandes collections, cette approche pourrait être assez prohibitive.
Selon la documentation Python ( ici ), c'est le plus proche que vous puissiez obtenir avec Java, et ce n'est plus verbeux:
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]);
}
Si vous devez utiliser la classe List
...
List<String> numbers = Arrays.asList("zero", "one", "two");
for (int i = 0; i < numbers.size(); i++) {
System.out.println(i + " " + numbers.get(i));
}
* REMARQUE: si vous devez modifier la liste au fur et à mesure de votre navigation, vous devrez utiliser l'objet Iterator, car il a la possibilité de modifier la liste sans générer une ConcurrentModificationException
.
List<String> list = { "foo", "bar", "foobar"};
int i = 0;
for (String str : list){
System.out.println(i++ + str );
}
En combinant des génériques avec des interfaces anonymes, vous pouvez essentiellement créer une méthode d'usine pour gérer l'énumération. Le rappel de l'énumérateur masque le désordre de l'itérateur situé en dessous.
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);
}
});
}
}
[0] John
[1] Paul
[2] George
[3] Ringo
Je suis allé plus loin et j'ai créé des fonctions de cartographie, de réduction et de filtrage basées sur ce concept.
Les dépendances de Guava et Apache common-collections de Google incluent des fonctionnalités similaires. Vous pouvez les vérifier comme vous le souhaitez.
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));
}
}
Non. Il existe peut-être des bibliothèques pour prendre en charge une telle fonctionnalité. Mais si vous avez recours aux bibliothèques standard, il vous appartient de compter.
Je pense que cela devrait être la fonctionnalité Java qui ressemble le plus à "énumérer" python, bien que ce soit assez compliqué et inefficace. Fondamentalement, il suffit de mapper les index de la liste avec ses éléments, en utilisant ListIterator ou 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());
}
ou en utilisant l'expression lambda:
Set<Integer, String> enumeration = IntStream.range(0, list.size()).boxed.collect(Collectors.toMap(index -> index, index -> list.get(index)));
alors vous pouvez l'utiliser avec une boucle for améliorée:
for (Map.Entry<Integer, String> entry : enumeration.entrySet){
System.out.println(entry.getKey() + "\t" + entry.getValue());
}
Simple et direct
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++;
}
}
Exemple d'utilisation:
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));
});
}
Désormais, avec l’API Java 8s Stream et la petite bibliothèque ProtonPack
, fournissant StreamUtils
, elle peut être réalisée facilement.
Le premier exemple utilise la même notation pour chaque notation que dans la 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());
}
Ci-dessus, bien que ne fournisse pas l’évaluation lazy comme dans Python . Pour cela, vous devez utiliser la méthode forEach()
Stream API:
Stream<String> numbers = Arrays.stream("zero one two".split(" "));
StreamUtils.zipWithIndex(numbers)
.forEach(n -> System.out.println(n.getIndex() + " " + n.getValue()));
L'évaluation paresseuse peut être vérifiée avec le flux infini suivant:
Stream<Integer> infStream = Stream.iterate(0, i -> i++);
StreamUtils.zipWithIndex(infStream)
.limit(196)
.forEach(n -> System.out.println(n.getIndex() + " " + n.getValue()));