Für Sammlungen, die die List
Schnittstelle implementieren , können Sie die listIterator()
Methode aufrufen , um eine zu erhalten ListIterator
. Der Iterator hat (unter anderem) zwei Methoden - nextIndex()
um den Index zu erhalten; und next()
, um den Wert zu erhalten (wie bei anderen Iteratoren).
Ein Java-Äquivalent zu Python könnte also sein:
List<String> numbers = Arrays.asList("zero", "one", "two");
ListIterator<String> it = numbers.listIterator();
while (it.hasNext()) {
System.out.println(it.nextIndex() + " " + it.next());
}
was wie der Python ausgibt:
0 zero
1 one
2 two
it.next()
Hat also ein Nebeneffekt? Ist es garantiert sicher zu mischenit.nextIndex()
undit.next()
im gleichen Ausdruck?next()
den Nebeneffekt, dass der Iterator um ein Element erweitert wird. Die Java-Sprachspezifikation garantiert jedoch, dass die Operanden für den+
Operator von links nach rechts ausgewertet werden. Siehe Abschnitt 15.7 .enumerate
funktioniert ganz anders. Pythonsenumerate
indiziert eine beliebige Sequenz unabhängig von ihrem internen Indexstatus. Es ergibt eine iterierbare 'Ersatz'-Sequenz mit (Index-, Element-) Paaren als Elementen. Es akzeptiert einenstart
Parameter, der dem Index einen Offset hinzufügt - kann aber trotzdem in der Schleife ausgeführt werden. Es funktioniert nativ mit den for-each like-Schleifen.Ich finde, dass dies dem Python-Ansatz am ähnlichsten ist.
Verwendung
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); } }
Ausgabe
0 zero 1 one 2 two 3 zero 4 one 5 two
Eigenschaften
Implementierung
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); } }
quelle
Genau genommen, nein, da die Funktion enumerate () in Python eine Liste von Tupeln zurückgibt und Tupel in Java nicht vorhanden sind.
Wenn jedoch alles , was Sie daran interessiert sind , ist das Drucken eines Index und einen Wert aus, dann können Sie den Vorschlag von Richard Fearn & Nutzung nextindex () folgen und next () auf einem Iterator.
Beachten Sie auch, dass enumerate () mit der allgemeineren Funktion zip () (mit Python-Syntax) definiert werden kann:
mylist = list("abcd") zip(range(len(mylist)), mylist)
ergibt [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')]
Wenn Sie Ihre eigene Tupel- Klasse definieren (siehe Verwenden von Paaren oder 2-Tupeln in Java als Ausgangspunkt), können Sie sicherlich leicht Ihre eigene zip () -Funktion in Java schreiben, um sie zu verwenden (unter Verwendung der in der Tabelle definierten Tupel-Klasse) Verknüpfung):
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; }
Und sobald Sie zip () haben, ist die Implementierung von enumerate () trivial.
Bearbeiten: langsamer Tag bei der Arbeit, um es zu beenden:
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: Wie in den Kommentaren zu dieser Frage ausgeführt, ist dies nicht ganz gleichwertig. Es erzeugt zwar dieselben Werte wie die Aufzählung von Python, jedoch nicht auf dieselbe generative Weise wie die Aufzählung von Python. Daher könnte dieser Ansatz für große Sammlungen ziemlich unerschwinglich sein.
quelle
enumerate
gibt keine Liste von Tupeln zurück. es gibt ein Auflisten Objekt ' , das ist iterable seitenumerate
wurde entworfen a seinen Generator .zip
undrange
erstellt eine Liste, die auf sehr großen Listen speichereffizient ist. Iteratoren wie Enumerate behandeln nur das aktuelle Element und eine Funktion, um das nächste zu generieren. In Python 2.x gibt es itertools.izip und xrange , die genauer emuliert werden könnenenumerate
.Laut den Python-Dokumenten ( hier ) ist dies das Beste, was Sie mit Java erreichen können, und es ist nicht mehr ausführlich:
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]); }
Wenn Sie die
List
Klasse verwenden müssen ...List<String> numbers = Arrays.asList("zero", "one", "two"); for (int i = 0; i < numbers.size(); i++) { System.out.println(i + " " + numbers.get(i)); }
* HINWEIS: Wenn Sie die Liste während des Durchlaufens ändern müssen, müssen Sie das Iterator-Objekt verwenden, da es die Liste ändern kann, ohne ein zu erhöhen
ConcurrentModificationException
.quelle
List<String> list = { "foo", "bar", "foobar"}; int i = 0; for (String str : list){ System.out.println(i++ + str ); }
quelle
i++
ist es keine gute Wahl, die in einer der Anweisungen zu haben, da dies zu schwer zu verfolgenden Fehlern führen kann, wenn diese Anweisung (bedingt) mehr als einmal übersprungen oder ausgeführt / kopiert wird. Besser eine Standleitung habeni++
.Jetzt mit Java 8s Stream API zusammen mit der kleinen
ProtonPack
Bibliothek, sofernStreamUtils
es einfach erreicht werden kann.Das erste Beispiel verwendet für jede Notation dieselbe wie in der Frage:
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()); }
Oben liefert jedoch nicht die faule Auswertung wie in Python. Dazu müssen Sie die
forEach()
Stream-API-Methode verwenden:Stream<String> numbers = Arrays.stream("zero one two".split(" ")); StreamUtils.zipWithIndex(numbers) .forEach(n -> System.out.println(n.getIndex() + " " + n.getValue()));
Die verzögerte Bewertung kann mit dem folgenden unendlichen Strom überprüft werden:
Stream<Integer> infStream = Stream.iterate(0, i -> i++); StreamUtils.zipWithIndex(infStream) .limit(196) .forEach(n -> System.out.println(n.getIndex() + " " + n.getValue()));
quelle
Einfach und unkompliziert
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++; } }
Beispielnutzung:
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)); }); }
quelle
Nein. Vielleicht gibt es einige Bibliotheken, die eine solche Funktionalität unterstützen. Wenn Sie jedoch auf die Standardbibliotheken zurückgreifen, müssen Sie zählen.
quelle
Ich denke, dies sollte die Java-Funktionalität sein, die der Python-Aufzählung am meisten ähnelt, obwohl sie ziemlich kompliziert und ineffizient ist. Ordnen Sie die Indizes der Liste einfach mit ListIterator oder Collector ihren Elementen zu:
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()); }
oder mit Lambda-Ausdruck:
Set<Integer, String> enumeration = IntStream.range(0, list.size()).boxed.collect(Collectors.toMap(index -> index, index -> list.get(index)));
dann können Sie es mit einer erweiterten for-Schleife verwenden:
for (Map.Entry<Integer, String> entry : enumeration.entrySet){ System.out.println(entry.getKey() + "\t" + entry.getValue()); }
quelle
Durch die Kombination von Generika mit anonymen Schnittstellen können Sie im Wesentlichen eine Factory-Methode für die Übergabe von Aufzählungen erstellen. Der Enumerator-Rückruf verbirgt die Unordnung des darunter liegenden Iterators.
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); } }); } }
Ergebnis
[0] John [1] Paul [2] George [3] Ringo
Erweiterte Gedanken
Map, Reduce, Filter
Ich bin noch einen Schritt weiter gegangen und habe auf der Grundlage dieses Konzepts Funktionen zum Zuordnen, Reduzieren und Filtern erstellt.
Die allgemeinen Sammlungen von Google für Guava und Apache enthalten ähnliche Funktionen. Sie können sie nach Belieben auschecken.
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)); } }
quelle
Ziemlich die gleiche Syntax mit 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); });
quelle