Java 8 - 数值流Numberic Stream
文章目錄
- Pre
- 原始類型流特化
- 映射到數值流 (mapToInt 、 mapToDouble 和 mapToLong )
- 轉換回數值流 boxed
- 默認值OptionalInt
- 數值范圍( range 和 rangeClosed)
- 小結
- 附
Pre
Java 8 - Stream流騷操作解讀2_歸約操作操作了reduce, 使用 reduce 方法計算流中元素的總和.
來看個例子
// 菜單中的熱量求和public static Integer sumCal(List<Dish> dishes){return dishes.stream().map(Dish::getCalories).reduce(0,Integer::sum);}功能肯定是OK的。 不過這里暗含了一個裝箱成本。 每個 Integer 都必須被拆箱成一個原始類型,再進行求和。
如果可以直接像下面這樣調用 sum 方法,不是更好嗎?
dishes.stream().map(Dish::getCalories).sum()但這是不可能的。問題在于 map 方法會生成一個 Stream<T> 。雖然流中的元素是 Integer 類型,但 Streams 接口沒有定義 sum 方法。
Stream API還提供了原始類型流特化,專門支持處理數值流的方法。
原始類型流特化
Java 8引入了三個原始類型特化流接口來解決這個問題: IntStream 、 DoubleStream 和LongStream ,分別將流中的元素特化為 int 、 long 和 double ,從而避免了暗含的裝箱成本。
每個接口都帶來了進行常用數值歸約的新方法,比如對數值流求和的 sum ,找到最大元素的 max 。
此外還有在必要時再把它們轉換回對象流的方法。要記住的是,這些特化的原因并不在于流的復雜性,而是裝箱造成的復雜性——即類似 int 和 Integer 之間的效率差異。
映射到數值流 (mapToInt 、 mapToDouble 和 mapToLong )
將流轉換為特化版本的常用方法是 mapToInt 、 mapToDouble 和 mapToLong 。
這些方法和前面說的 map 方法的工作方式一樣,只是它們返回的是一個特化流,而不是 Stream<T> 。
例如,可以像下面這樣用 mapToInt 對 menu 中的卡路里求和:
這里, mapToInt 會從每道菜中提取熱量(用一個 Integer 表示),并返回一個 IntStream(而不是一個 Stream<Integer> )。然后就可以調用 IntStream 接口中定義的 sum 方法,對卡路里求和了!
請注意,如果流是空的, sum 默認返回 0 。 IntStream 還支持其他的方便方法,如max 、 min 、 average 等。
轉換回數值流 boxed
一旦有了數值流,你可能會想把它轉換回非特化流。例如, IntStream 上的操作只能產生原始整數: IntStream 的 map 操作接受的Lambda必須接受 int 并返回 int (一個IntUnaryOperator )。但是你可能想要生成另一類值,比如 Dish 。為此,你需要訪問 Stream接口中定義的那些更廣義的操作。要把原始流轉換成一般流(每個 int 都會裝箱成一個Integer ),可以使用 boxed 方法
需要將數值范圍裝箱成為一個一般流時, boxed 尤其有用。
默認值OptionalInt
求和的那個例子很容易,因為它有一個默認值: 0 。但是,如果你要計算 IntStream 中的最大元素,就得換個法子了,因為 0 是錯誤的結果。如何區分沒有元素的流和最大值真的是 0 的流呢?
前面我們介紹了 Optional 類,這是一個可以表示值存在或不存在的容器。 Optional 可以用Integer 、 String 等參考類型來參數化。對于三種原始流特化,也分別有一個 Optional 原始類型特化版本: OptionalInt 、 OptionalDouble 和 OptionalLong 。
例如,要找到 IntStream 中的最大元素,可以調用 max 方法,它會返回一個 OptionalInt :
OptionalInt max = dishes.stream().mapToInt(Dish::getCalories).max();現在,如果沒有最大值的話,你就可以顯式處理 OptionalInt 去定義一個默認值了:
數值范圍( range 和 rangeClosed)
和數字打交道時,有一個常用的東西就是數值范圍。比如,假設你想要生成1和100之間的所有數字。Java 8引入了兩個可以用于 IntStream 和 LongStream 的靜態方法,幫助生成這種范圍range 和 rangeClosed 。
這兩個方法都是第一個參數接受起始值,第二個參數接受結束值。但range 是不包含結?值的,而 rangeClosed 則包含結束值。
這里我們用了 rangeClosed 方法來生成1到100之間的所有數字。它會產生一個流,然后你可以鏈接 filter 方法,只選出偶數。到目前為止還沒有進行任何計算。最后,你對生成的流調用 count 。因為 count 是一個終端操作,所以它會處理流,并返回結果 50 ,這正是1到100(包括兩端)中所有偶數的個數。請注意,比較一下,如果改用 IntStream.range(1, 100) ,則結果將會是 49 個偶數,因為 range 是不包含結?值的。
小結
事實上,流讓你可以簡潔地表達復雜的數據處理查詢。此外,流可以透明地并行化
- 使用 filter 、 distinct 、 skip 和 limit 對流做篩選和切片。
- 使用 map 和 flatMap 提取或轉換流中的元素
- 使用 findFirst 和 findAny 方法查找流中的元素。你可以用 allMatch 、noneMatch 和 anyMatch 方法讓流匹配給定的謂詞。
- 這些方法都利用了短路:找到結果就立即停止計算;沒有必要處理整個流。
- 以利用 reduce 方法將流中所有的元素迭代合并成一個結果,例如求和或查找最大元素。
- filter 和 map 等操作是無狀態的,它們并不存儲任何狀態。 reduce 等操作要存儲狀態才能計算出一個值。 sorted 和 distinct 等操作也要存儲狀態,因為它們需要把流中的所有元素緩存起來才能返回一個新的流。這種操作稱為有狀態操作
- 流有三種基本的原始類型特化: IntStream 、 DoubleStream 和 LongStream 。它們的操作也有相應的特化。
- 流不僅可以從集合創建,也可從值、數組、文件以及 iterate 與 generate 等特定方法創建。
- 無限流是沒有固定大小的流。
附
測試數據
List<Dish> menu = Arrays.asList(new Dish("pork", false, 800, Dish.Type.MEAT),new Dish("beef", false, 700, Dish.Type.MEAT),new Dish("chicken", false, 400, Dish.Type.MEAT),new Dish("french fries", true, 530, Dish.Type.OTHER),new Dish("rice", true, 350, Dish.Type.OTHER),new Dish("season fruit", true, 120, Dish.Type.OTHER),new Dish("pizza", true, 550, Dish.Type.OTHER),new Dish("prawns", false, 300, Dish.Type.FISH),new Dish("salmon", false, 450, Dish.Type.FISH));總結
以上是生活随笔為你收集整理的Java 8 - 数值流Numberic Stream的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java 8 - Stream实战
- 下一篇: MySQL - mysqldump多种方