2017-03-03 5 views
1

Ich habe einen Strom von Enum-Werten, die ich reduzieren möchte. Wenn der Stream leer ist oder andere Werte enthält, möchte ich null. Wenn es nur einen einzelnen Wert (mehrere Instanzen) enthält, möchte ich diesen Wert.Warum wirft Stream.reduce (BinaryOperator) NullPointer, wenn das Ergebnis null ist?

[]    null 
[A, B, A]  null 
[A]    A 
[A, A, A]  A 

Ich versuchte es mit einem reduzieren zu tun:

return <lots of filtering, mapping, and other stream stuff> 
     .reduce((enum1, enum2) -> enum1 == enum2 ? enum1 : null) 
     .orElse(null); 

Leider ist dies nicht funktioniert, weil diese Methode eine NullPointerException wirft reduzieren, wenn das Ergebnis null ist. Weiß jemand, warum das passiert? Warum ist null kein gültiges Ergebnis?

Vorerst Ich löste dies wie folgt aus:

MyEnum[] array = <lots of filtering, mapping, and other stream stuff> 
       .distinct() 
       .toArray(MyEnum[]::new); 
return array.length == 1 ? array[0] : null; 

Während dies funktioniert, ich bin nicht zufrieden mit diesem „Umweg“. Mir gefiel das Reduzieren, weil es schien, als wäre es das Richtige und legte alles in einen Stream.

Kann jemand an eine Alternative zum Reduzieren denken (das ist im Idealfall nicht zu viel Code)?

+0

Diese Antwort könnte Ihnen einen Einblick geben, warum http://StackOverflow.com/a/17115490/1544715 – Magnus

Antwort

1

Im Allgemeinen werden alle Stream-Methoden eine Optional Rückkehr erlauben nicht null Werte, da es unmöglich wäre, die null Ergebnis und „kein Ergebnis“ (leerer Strom) auseinander zu halten.

können Sie arbeiten-um diese mit einem Platzhalter-Wert, der leider die Typsicherheit zu suspendieren erfordert (da es keine Typ-kompatiblen Wert außerhalb des ENUM-Set ist):

return <lots of filtering, mapping, and other stream stuff> 
    .reduce((enum1, enum2) -> enum1 == enum2? enum1: "") 
    .map(r -> r==""? null: (MyEnum)r) 
    .orElse(null); 

Optional.map zurückkehren ein leeres optionales wenn die Mapping-Funktion null zurückgibt, so dass nach diesem Schritt ein leerer Stream und ein null Ergebnis nicht mehr unterschieden werden kann und orElse(null) in beiden Fällen null zurückgibt.

Aber vielleicht fühlt sich der Array-Umweg nur unbefriedigend, weil das Array nicht die beste Wahl für das Zwischenergebnis ist? Wie wäre es

EnumSet<MyEnum> set = <lots of filtering, mapping, and other stream stuff> 
    .collect(Collectors.toCollection(() -> EnumSet.noneOf(MyEnum.class))); 
return set.size()==1? set.iterator().next(): null; 

Die EnumSet ist nur ein bitset, einen einzigen long Wert, wenn der enum Typ nicht mehr als 64 Konstanten hat.Das ist viel billiger als ein Array und da Set s von Natur aus verschieden sind, besteht keine Notwendigkeit für einen distinct() Betrieb am Strom, der unter der Haube einen HashSet erzeugen würde.

+0

Danke für die Erklärung. Ich verstehe den Grund, den Sie geben, obwohl die Reduzierung, die ich versuchte, immer noch sehr nett aussah: D. Ich habe versucht, mit 'toSet()' zu sammeln, bevor ich auf das Array umschaltete (dachte, es sei prägnanter und schneller möglich). Ich kannte das EnumSet nicht, ich werde es versuchen. –

0

Die meisten Stromfunktionen höherer Ordnung erlauben null nicht als Rückgabewerte für Parameter oder Funktionen. Sie sollen noch eine weitere billion-dollar mistake verhindern. Eine solche Reaktion ist here dokumentiert:

Optional reduzieren (BinaryOperator Akkumulator)

.....

Wirft: Nullpointer - wenn das Ergebnis der Reduktion null ist

0

Wie wäre es mit einem wirklich mathematischen Ansatz (schwer zu pflegen, stimme ich zu)?

Arrays.stream(array).map(e -> e.ordinal() + 1).reduce(Integer::sum) 
      .map(i -> (double) i/array.length == array[0].ordinal() + 1 ? array[0] : null) 
      .orElse(null) 
+0

Es gibt kein Array zu beginnen und das Generieren eines Arrays ist bereits Teil der Work-around-OP loswerden. – Holger

Verwandte Themen