Iterieren Sie eine Aufzählung in Java 8

73

Ist es möglich, eine Enumerationmit Lambda-Ausdruck zu iterieren ? Was ist die Lambda-Darstellung des folgenden Code-Snippets:

Enumeration<NetworkInterface> nets = NetworkInterface.getNetworkInterfaces();

while (nets.hasMoreElements()) {
    NetworkInterface networkInterface = nets.nextElement();

}

Ich habe keinen Stream darin gefunden.

Tapas Bose
quelle
7
Aufzählungen wurden 1998 in Java 1.2 durch Iteratoren ersetzt. Es ist bedauerlich, dass Sie sie noch verwenden müssen. : |
Peter Lawrey
1
Sie können Ihre Aufzählung an Iterator anpassen: stackoverflow.com/questions/5007082/…
marcinj
7
@PeterLawrey Iterator kam mit, aber es gibt immer noch Kern-APIs, die Enumeration verwenden. Generics kamen hinzu, aber es gibt immer noch Kern-APIs, die nackte Typen verwenden oder Object zurückgeben. Lambdas kam mit, aber Swing ist immer noch voll von Schnittstellen mit mehreren Methoden, daher können wir sie nicht verwenden. Es überrascht mich nicht mehr wirklich, wenn ich APIs finde, deren Modernisierung Java vergessen hat. es macht mich nur traurig.
Trejkaz
3
@Trejkaz Ja, Swing verwendet immer noch Vector und Hashtable.
Peter Lawrey
@PeterLawrey Die Servlet-API verwendet weiterhin Aufzählungen.
OrangeDog

Antworten:

36

Falls Ihnen die Tatsache nicht gefällt, dass Collections.list(Enumeration)der gesamte Inhalt vor Beginn der Iteration in eine (temporäre) Liste kopiert wird, können Sie sich mit einer einfachen Dienstprogrammmethode helfen:

public static <T> void forEachRemaining(Enumeration<T> e, Consumer<? super T> c) {
  while(e.hasMoreElements()) c.accept(e.nextElement());
}

Dann können Sie einfach forEachRemaining(enumeration, lambda-expression);(beachten Sie die import staticFunktion) ...

Holger
quelle
87

(Diese Antwort zeigt einen von vielen Optionen Nur weil ist. Hat Zeichen für die Annahme hat, bedeutet nicht , es ist die beste, die ich andere Antworten empfehlen zu lesen und Kommissionierung je nach Situation Sie sind in IMO:..
- für Java 8 Holger Antwort schönsten ist, denn abgesehen von einfach ist es nicht zusätzliche Iteration benötigt , die in meiner Lösung geschieht.
- für Java 9 würde ich Lösung von Pick Tagir Valeev Antwort )


Sie können Elemente von Ihrem Enumerationnach ArrayListmit kopieren Collections.listund dann wie folgt verwenden

Collections.list(yourEnumeration).forEach(yourAction);
Pshemo
quelle
16
Der Haken ist, dass Sie jetzt alle Elemente in einer Liste speichern müssen. Was, wenn es klein ist, wahrscheinlich in Ordnung ist. Aber es ist erwähnenswert, weil es manchmal zu groß ist, um in die Erinnerung zu passen.
Trejkaz
Wie wäre es mit java.util.stream.Stream # of (yourEnumeration.values ​​()). ForEach ()?
Filip
3
@Filip Dein Kommentar ist ungefähr, Enumaber die Frage ist ungefähr Enumeration.
Pshemo
51

Wenn Ihr Code viele Aufzählungen enthält, empfehle ich, eine statische Hilfsmethode zu erstellen, die eine Aufzählung in einen Stream konvertiert . Die statische Methode könnte wie folgt aussehen:

public static <T> Stream<T> enumerationAsStream(Enumeration<T> e) {
    return StreamSupport.stream(
        Spliterators.spliteratorUnknownSize(
            new Iterator<T>() {
                public T next() {
                    return e.nextElement();
                }
                public boolean hasNext() {
                    return e.hasMoreElements();
                }
            },
            Spliterator.ORDERED), false);
}

Verwenden Sie die Methode mit einem statischen Import . Im Gegensatz zur Lösung von Holger können Sie von den verschiedenen Stream- Vorgängen profitieren , wodurch der vorhandene Code möglicherweise noch einfacher wird. Hier ist ein Beispiel:

Map<...> map = enumerationAsStream(enumeration)
    .filter(Objects::nonNull)
    .collect(groupingBy(...));
nosid
quelle
Ich denke, beide ergänzen sich genau wie list.forEach(…)und list.stream(). ⟨stream ops⟩ .forEach(…).
Holger
1
Wie ist das nicht Teil des JDK?
Nick
public static <T> Stream<T> enumerationAsStream(Enumeration<T> e) { return Collections.list(e).stream(); }würde das gleiche tun, aber viel einfacher.
Thomas Fritsch
@ ThomasFritschs Vorschlag macht eine Zwischenkopie der Liste, was vermieden wird.
Chrylis
46

Seit Java-9 wird es eine neue Standardmethode geben, Enumeration.asIterator()die die reine Java-Lösung vereinfacht:

nets.asIterator().forEachRemaining(iface -> { ... });
Tagir Valeev
quelle
11

Sie können die folgende Kombination von Standardfunktionen verwenden:

StreamSupport.stream(Spliterators.spliteratorUnknownSize(CollectionUtils.toIterator(enumeration), Spliterator.IMMUTABLE), parallel)

Sie können auch weitere Merkmale wie NONNULLoder hinzufügen DISTINCT.

Nach dem Anwenden statischer Importe wird dies besser lesbar:

stream(spliteratorUnknownSize(toIterator(enumeration), IMMUTABLE), false)

Jetzt haben Sie einen Standard-Java 8-Stream, der auf jede Weise verwendet werden kann! Sie können truefür die parallele Verarbeitung übergeben.

Verwenden Sie eine der folgenden Optionen, um von Enumeration zu Iterator zu konvertieren:

  • CollectionUtils.toIterator() ab Spring 3.2 oder Sie können verwenden
  • IteratorUtils.asIterator() aus Apache Commons-Sammlungen 3.2
  • Iterators.forEnumeration() von Google Guava
Arne Burmeister
quelle
1
Woher kommt CollectionUtils? Scheint nicht im JDK zu sein.
Martin
@ Martin, es scheint zu sein org.apache.commons.collections.CollectionUtils.
Vadzim
@Vadzim Ich habe Apache Commons Collections 1.0, 2.0, 2.1.1, 3.2.1 und 4.0 überprüft , aber keine Klasse mit dem Namen CollectionUtils enthält eine Methode mit dem Namen toIterator.
Martin
Sorry, war verwirrt, bekam es von Spring 3.2
Arne Burmeister
7

Für Java 8 ist die einfachste Umwandlung der Aufzählung in Stream:

Collections.list(NetworkInterface.getNetworkInterfaces()).stream()

Marek Gregor
quelle
4

Ich weiß, dass dies eine alte Frage ist, aber ich wollte eine Alternative zu den Funktionen Collections.asList und Stream vorstellen. Da die Frage den Titel "Iteration an Enumeration" trägt, wird manchmal ein Lambda-Ausdruck verwendet, aber eine erweiterte for-Schleife ist möglicherweise vorzuziehen, da das aufgezählte Objekt möglicherweise eine Ausnahme auslöst und die for-Schleife in einem größeren Versuch einfacher zu kapseln ist. Fangcodesegment (Lambdas erfordern deklarierte Ausnahmen, um innerhalb des Lambda gefangen zu werden). Zu diesem Zweck wird hier ein Lambda verwendet, um eine Iterable zu erstellen, die in einer for-Schleife verwendet werden kann und die Aufzählung nicht vorlädt:

 /**
 * Creates lazy Iterable for Enumeration
 *
 * @param <T> Class being iterated
 * @param e Enumeration as base for Iterator
 * @return Iterable wrapping Enumeration
 */
public static <T> Iterable<T> enumerationIterable(Enumeration<T> e)
{
    return () -> new Iterator<T>()
    {
        @Override
        public T next()
        {
            return e.nextElement();
        }

        @Override
        public boolean hasNext()
        {
            return e.hasMoreElements();
        }
    };
}
user579013
quelle