2014-11-08 2 views
5

Ich bin ein großer Fan des Stream-Operators singleOrEmpty. Es ist nicht in der Standardbibliothek, aber ich finde es sehr nützlich. Wenn ein Stream nur einen einzelnen Wert hat, gibt er diesen Wert in Optional zurück. Wenn es keine Werte oder mehr als einen Wert hat, gibt es Optional.empty() zurück.Java 8 Spliterator (oder ähnlich), der einen Wert zurückgibt, wenn es nur einen einzigen Wert gibt

Optional<Int> value = someList.stream().{singleOrEmpty} 
[]  -> Optional.empty() 
[1] -> Optional.of(1) 
[1, 1] -> Optional.empty() 
etc. 

I asked a question about it earlier und @ThomasJungblut came up with this great implementation:

public static <T> Optional<T> singleOrEmpty(Stream<T> stream) { 
    return stream.limit(2) 
     .map(Optional::ofNullable) 
     .reduce(Optional.empty(), 
      (a, b) -> a.isPresent()^b.isPresent() ? b : Optional.empty()); 
} 

Das einzige Problem ist, müssen Sie es zu Beginn des Gesprächs am Ende

singleOrEmpty(someList.stream().filter(...).map(...)) 

anstatt nacheinander setzen

someList.stream().filter().map().singleOrEmpty() 

, die es schwieriger zu lesen als andere Stream-Mechanismen macht.

So als Neuling zu dieser Verarbeitung Sachen Strom, hat jemand irgendwelche Tricks, wie ein Kurzschließen singleOrEmpty Mechanismus am Ende eine Folge von Strom Transformationen setzen geht über?

+0

ich denke, das 8, ähnliche Geschichte hier in Java nicht möglich ist (http://stackoverflow.com/questions/22308823/extending-listt-in-java-8) –

Antwort

6

Es wird nicht so schnell wie die Begrenzung (2), aber man kann es als list.stream().filter(...).map(...).collect(singleOrEmpty())

verwenden
static <T> Collector<T, ?, Optional<T>> singleOrEmpty() { 
    return Collectors.collectingAndThen(
      Collectors.mapping(
        Optional::of, 
        Collectors.reducing((a, b) -> Optional.empty()) 
      ), 
      o -> o.orElseGet(Optional::empty) 
    ); 
} 

Stream.empty().collect(singleOrEmpty()); // Optional.empty 
Stream.of(1).collect(singleOrEmpty());  // Optional[1] 
Stream.of(1, 1).collect(singleOrEmpty()); // Optional.empty 
Stream.of(1, 1).skip(1).collect(singleOrEmpty()); // Optional[1] 

Für das, was es wert ist, es sei denn, dies wirklich leistungskritischen Code ist, würde ich persönlich lieber die weniger klug, aber viel klarere Umsetzung

static<T> Collector<T,?,Optional<T>> singleOrEmpty() { 
    return Collectors.collectingAndThen(
      Collectors.toList(), 
      lst -> lst.size() == 1 
        ? Optional.of(lst.get(0)) 
        : Optional.empty() 
    ); 
} 
+2

Erste Collector kann mit überladener Version von 'Collectors.reduci 'vereinfacht werden ng (...) '. Ich denke, diese Version ist nicht schlechter als die zweite, die du vorgeschlagen hast (Entschuldigung für das große Stück Code, ich denke nur nicht, dass es eine separate Antwort wert ist). 'statisch Collector ?> SingleOrEmptyCollector() { return Collectors.reducing ( Optional.empty(), Optional :: ofNullable, (opt, t) -> opt.isPresent()^t.isPresent()? t: Optional.empty() ); } ' –

+0

Außerdem gibt es noch etwas zu erwähnen. Wenn eine Pipeline eine teure Operation hat (wie map ((a) -> doSomethingReallySlow (a)) '), müssen wir sie immer noch auf ALLEN Elementen ausführen, auch wenn wir nur eine wollen. 'limit (2)' kann uns retten, aber nicht in allen Fällen. Siehe [Limit-Dokumentation] (http://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#limit-long-) für Details. Seien Sie also vorsichtig und erwarten Sie nicht, dass dieser Kollektor alle Elemente bis auf eins überspringt (oder zumindest wie 'stream.of (...). Limit (2) .collect (singleOrEmpty())'). –

+2

@StanislavLukyanov Ihr Kollektor wird fehlschlagen, wenn der Stream 3 (oder 5, 7 usw.) Elemente enthält. Zum Beispiel gibt 'Stream.of (1,1,1) .collect (...)' optional [1] anstelle von Optional.empty zurück. – Misha