Guava und JDK Stream-Versionen unten, aber der absolute Gewinner ist JavaSlang. Ich liebe es!
import javaslang.collection.Stream;
import java.util.Optional;
// ...
default X root(X node) {
return Stream.iterate(Optional.of(node),
t -> t.flatMap(this::parent)
).takeWhile(Optional::isPresent)
.last()
.orElseThrow(NoSuchElementException::new);
}
Die Stream.iterate()
Methode ist genau das, was ich in den beiden anderen Versionen bin fehlt.
Guava:
Zuerst werden wir eine Hilfsklasse Optionals
genannt erstellen:
// to avoid misunderstandings:
import java.util.Optional;
import java.util.function.Function;
import com.google.common.collect.AbstractIterator;
public final class Optionals {
private Optionals(){}
/**
* Given an instance of type T and a function from T to Optional<T>,
* return an Iterable<T>. This Iterable will keep returning values
* by repeatedly applying the supplied function until the returned Optional
* is not present.
*/
public static <T> Iterable<T> stream(
final T start, final Function<T, Optional<T>> increment) {
return() -> new AbstractIterator<T>() {
Optional<T> current = Optional.of(start);
@Override
protected T computeNext() {
if (!current.isPresent()) return endOfData();
final T data = current.get();
current = current.flatMap(increment);
return data;
}
};
}
}
Das ist natürlich noch mehr Textvorschlag, aber das ist wiederverwendbar, und jetzt sind wir kann die Schnittstellenmethode übersichtlich schreiben:
default X root(final X node) {
return Iterables.getLast(Optionals.stream(node, this::parent));
}
Offensichtlich wäre es möglich, dies ohne Guava zu implementieren, aber es wäre viel unordentlicher.
Java 8 Stream-Version:
public final class Streams {
private Streams(){}
/**
* Given an instance of type T and a function from T to Optional<T>,
* return a Stream<T>. This Stream will keep returning values by
* repeatedly applying the supplied function until the returned Optional
* is not present.
*/
public static <X> Stream<X> stream(
final X start, final Function<X, Optional<X>> increment) {
return StreamSupport.stream(
new Spliterator<X>() {
Optional<X> next = Optional.ofNullable(start);
@Override
public boolean tryAdvance(final Consumer<? super X> action) {
final boolean present = next.isPresent();
if (present) action.accept(next.get());
next = next.flatMap(increment);
return present;
}
@Override
public Spliterator<X> trySplit() { return null; }
@Override
public long estimateSize() { return Long.MAX_VALUE; }
@Override
public int characteristics() { return Spliterator.ORDERED; }
}, false
);
}
, die zu diesem Code führt:
default X root(final X node) {
return Streams.stream(node, this::parent)
.reduce((left, right) -> right)
.orElseThrow(NoSuchElementException::new);
}
Yup, definitiv sauberer als meine ursprüngliche Version, danke. Es gibt keine Möglichkeit, dass NULL jemals hierher kommt, also ist das in Ordnung. –