2014-11-08 10 views
5

Jestem wielkim fanem operatora strumienia singleOrEmpty. Nie ma go w std lib, ale uważam, że jest bardzo użyteczny. Jeśli strumień ma tylko jedną wartość, zwraca tę wartość w postaci Optional. Jeśli nie ma wartości lub więcej niż jedną wartość, zwraca Optional.empty().Java 8 Spliterator (lub podobny), który zwraca wartość iff jest tylko jedna wartość

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

I asked a question about it earlier i @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()); 
} 

Jedynym problemem jest to, trzeba umieścić go na początku rozmowy

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

zamiast kolejno na końcu

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

co sprawia, że ​​trudniej jest go odczytać niż inne mechanizmy strumieniowe.

Więc jak nowo narodzony w tym strumieniu przetwarzania rzeczy, czy ktoś ma żadnych sztuczek, jak go o oddanie Zwarcie singleOrEmpty mechanizmu na końcu sekwencji przemian strumienia ?

+0

myślę, że to nie jest możliwe w Java 8, podobna historia tutaj (http://stackoverflow.com/questions/22308823/extending-listt-in-java-8) –

Odpowiedz

6

To nie będzie tak szybki jak jeden z limitem (2), ale można go używać jako list.stream().filter(...).map(...).collect(singleOrEmpty())

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] 

Na co warto, chyba że jest to naprawdę wydajność kod krytyczny, ja osobiście wolę mniej mądry, ale znacznie jaśniejsze wdrożenie

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

Pierwszej kolektor można uprościć przez przeciążoną wersję 'Collectors.reduci ng (...) '. Myślę, że ta wersja nie jest gorsza od drugiej, którą zasugerowałeś (przepraszam za duży kawałek kodu, po prostu nie sądzę, że warto osobnej odpowiedzi). 'static Collector ?> SingleOrEmptyCollector() {return Collectors.reducing ( Optional.empty(), Opcjonalnie :: ofNullable, (opt, t) -> opt.isPresent()^t.isPresent()? t: Opcjonalnie.empty() ); } ' –

+0

Należy również wspomnieć o jeszcze jednej rzeczy. Jeśli potok ma jakąś kosztowną operację (jak 'map ((a) -> doSomethingReallySlow (a)), to nadal musimy wykonać to na WSZYSTKICH elementach, nawet jeśli tylko chcemy. 'limit (2)' może nas uratować, ale nie we wszystkich przypadkach. Aby uzyskać szczegółowe informacje, patrz dokumentacja '' limit'' (http://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#limit-long-). Bądź więc ostrożny i nie oczekuj, że ten kolektor pominie wszystkie elementy oprócz jednego (lub przynajmniej użyje go jak 'stream.of (...). Limit (2) .collect (singleOrEmpty())'). –

+2

@StanislavLukyanov Twój kolektor nie powiedzie się, jeśli strumień ma 3 (lub 5, 7 itd.) Elementów. Na przykład 'Stream.of (1,1,1) .collect (...)' zwróci Opcjonalne [1] zamiast Opcjonalne.empty. – Misha

Powiązane problemy