Java >> Java Tutorial >  >> JDK

In JDK 9 (und gut 8) und darüber kann alles ein Stream sein

In JDK 8 konnten wir endlich Streams verwenden und alles war gut, abgesehen von den Zeiten, in denen die von Ihnen verwendete API keinen Stream erzeugen konnte. Dann haben Sie schließlich eine Wrapper-Klassenmethode geschrieben, mit der Sie einen Iterator in einen Stream umwandeln konnten, weil Sie Streams verpasst haben.

public static <T> Stream<T> asStream(Iterator<T> it) {
    return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it,
        Spliterator.IMMUTABLE | Spliterator.ORDERED),false);
}

Jetzt gibt es Methoden zum programmgesteuerten Generieren von Streams im Fall von Iterate und Generate, aber beide generieren einen unendlichen Stream, während Sie in den meisten Fällen wirklich eine vorhandene Schnittstelle in einen endlichen Stream anpassen wollten.

Dies wurde in JDK 9 durch die Einführung einer neuen Form von Iterationsmethode gut gelöst, mit der Sie ein Prädikat bereitstellen können, um das Ende des Streams zu signalisieren.

In den folgenden Beispielen werde ich ein Prädikat verwenden, das fortgesetzt wird, bis Sie einen Nulleintrag für den Stream erhalten. Ich überlasse es dem Leser, sich fantasievollere Verwendungen für das Prädikat auszudenken. In diesem einfachen Beispiel verwende ich die getCause-Methode von Throwable, um uns entlang einer verknüpften Fehlerliste zu bewegen. Beachten Sie, wie wenig Code dies im Vergleich zu einer Pre-Stream-Version benötigen würde.

// Simple linked list
//
Exception e = new Exception("one");
Exception e2 = new Exception("two",e);
Exception e3 = new Exception("three", e2);

Stream.iterate(e3, Objects::nonNull, Throwable::getCause)

    // Output the messages in turn
    .map(Throwable::getMessage)
    .forEach(System.out::println);

Das zweite Beispiel wandelt eine ReferenceQueue in einen Stream um, sodass wir ihren Inhalt problemlos zur Verarbeitung nach Bedarf entleeren können. Dieser Code ist ein wenig anders, weil der Container anders ist als das Objekt, an dem gearbeitet werden soll, also stellen wir den Startwert und den nächsten Wert mit derselben Methode bereit. Dies gibt null zurück, wenn die Warteschlange leer wird.

ReferenceQueue<Thing> queue = new ReferenceQueue<>();

// Make some things and then collect them
WeakReference one = new WeakReference<Thing>(new Thing(), queue);
WeakReference two = new WeakReference<Thing>(new Thing(), queue);
System.gc(); System.gc(); System.gc(); System.gc(); System.gc();

Stream.<Reference<? extends Thing>>iterate(
    queue.poll(), Objects::nonNull, v -> queue.poll())


    .forEach(System.out::println);

Das dritte Beispiel zeigt einen Spaziergang über einen Knotenbaum, beachten Sie den verschachtelten Stream-Iterator, um die Liste wieder hochzuarbeiten, wenn wir bis zum Ende eines Blattes gearbeitet haben.

Node root = doc.getDocumentElement();

Stream.iterate(
    root,
    Objects::nonNull,
    v -> {
        if (v.getFirstChild()!=null) {
            return v.getFirstChild();
        }

        if (v.getNextSibling()!=null) {
            return v.getNextSibling();
        }

        return Stream.iterate(v, Objects::nonNull, Node::getParentNode)
            .filter(node -> node.getNextSibling()!=null)
            .map(Node::getNextSibling).findFirst().orElse(null);
    })

    .map(Node::getNodeName)
    .forEach(System.out::println);

Mit ein wenig mentaler Gymnastik ist es also möglich, die meisten Legacy-APIs in einen netten sauberen Stream umzuwandeln, sodass Sie diese bösen altmodischen for-Schleifen ignorieren können. Und wenn Sie in JDK 8 feststecken, ist es ziemlich einfach, eine ähnliche Funktion mit dem asStream von vorher zusammenzustellen:

public static<T> Stream<T> iterateFinite(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next) {

    return asStream(new Iterator<>() {

        T current = seed;

        @Override
        public boolean hasNext() {
            return hasNext.test(current);
        }

        @Override
        public T next() {
            if (current == null) {
                throw new NoSuchElementException();
            }
            try {
                return current;
            } finally {
                current = next.apply(current);
            }
        }
    });
}

Java-Tag