web-dev-qa-db-fra.com

Existe-t-il un équivalent Java de la fonction 'énumération' de Python?

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? 

59
Richard Fearn

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
50
Richard Fearn

Je trouve que cela ressemble le plus à l’approche python.

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

Sortie

0 zero
1 one
2 two

3 zero
4 one
5 two

Caractéristiques

  • Fonctionne sur n'importe quel itérable
  • Ne crée pas de copie de liste en mémoire (convient aux grandes listes)
  • Prise en charge native pour chaque syntaxe
  • Accepte un paramètre de démarrage pouvant être ajouté à l'index

La mise en oeuvre

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

}
11
Pace

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.

8
Adam Parkin

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.

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

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

Résultat

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

Pensées prolongées

Carte, Réduire, Filtrer

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));
    }
}
1
Mr. Polywhirl

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.

1
rocksportrocker

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

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

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