2014-11-25 54 views
11

我是新來的Java的功能編程,並想知道我應該怎麼代碼,以避免NPE在(例如)此操作:呼叫流()降低()名單上只有一個元素

myList.stream() 
     .reduce((prev, curr) -> prev.getTimestamp().isAfter(curr.getTimestamp()) ? prev : curr); 
     .get().getTimestamp(); 

我的意圖是在列表中找到最新對象的時間戳。關於如何更好地收集最後一個元素的建議非常受歡迎,但我的主要問題實際上是爲什麼這是有效的。

的文件說,該函數拋出一個NullPointerException「如果減少的結果爲空」:

http://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#reduce-java.util.function.BinaryOperator-

這是確定的,但我不太明白的是爲什麼我不當這個代碼運行一個只包含一個元素的列表時,得到一個NullPointerException。我預計prev在這種情況下爲空。我嘗試過調試,但似乎只有一個元素時纔會遍歷整個lambda表達式。

回答

15

作爲reduce的JavaDoc說,減少相當於:

boolean foundAny = false; 
T result = null; 
for (T element : this stream) { 
    if (!foundAny) { 
     foundAny = true; 
     result = element; 
    } 
    else 
     result = accumulator.apply(result, element); 
} 
return foundAny ? Optional.of(result) : Optional.empty(); 

因此,如果流具有一個單一的元素,存在循環的僅一次迭代,並且在該迭代中找到的單個元件返回。

BinaryOperator只適用於Stream至少有兩個元素。

1

如果reduce操作實際上導致null值,則將拋出NPE。例如,如果您嘗試執行stream.reduce((a,b) -> null)

在另一方面,找到最新的時間戳,你可以這樣做:

myList.stream() 
    .map(t -> t.getTimeStamp()) 
    .max(Comparator.naturalOrder() 
    .get(); // will throw an exception if stream is empty 

或者,用靜態導入和假設你的類被稱爲事情:

myList.stream().map(Thing::getTimeStamp).max(naturalOrder()).get(); 
+0

請注意,這將只返回最新的時間戳,而不是具有最新時間戳的對象。 – skiwi 2014-11-25 16:58:23

+0

謝謝,我知道這一點,時間戳是我想要的。 – aweibell 2014-11-27 23:03:24

5

更好:

Optional<Thingie> max = 
     myList.stream() 
      .reduce(BinaryOperator.maxBy(comparing(Thingie::getTimeStamp)); 

reduce()這個過載返回一個Optional;一味地用get打開它是危險的,並且冒着NSEE的風險。用orElseorElseThrow等安全運營商之一解壓。

如果您的信息流中有空白,請先用.filter(t -> t != null)進行過濾。

+0

謝謝。我已經檢查過流不是空的,不認爲可能有空元素,但我想你的方法總是會更好。我明天會看。 – aweibell 2014-11-27 23:06:22