1、原始類型流特化
首先引入兩個概念
原始類型:int、double、byte、char
引用類型:Integer、Byte、Object、List
在Java中,①將原始類型轉換爲對應的引用類型的機制,這個機制叫做裝箱。②將引用類型轉換爲對應的原始類型,叫做拆箱。
但是在java中裝箱和拆箱是自動完成的,例如:
List<Integer> list = new ArrayList<>();
for(int i = 0 ; i < 100 ;i++){
list.add(i);
}
但是這麼做(int被裝箱成Integer)在性能方面是要付出代價的,裝箱的本質就是將原始類型包裹起來,並保存在堆裏。因此裝箱後的值需要更多的內存,並需要額外的內存搜索來獲取被包裹的原始值。
言歸正傳
重點:java8 引入了三個原始類型特化流來解決這個問題;IntStream、DoubleStream、LongStream 分別將流中元素特化爲int、double、long,從而避免了暗含裝箱的成本。每個接口都帶來了常用數值歸約的新方法,例如sum、max、min
案例變量:menu爲一個菜單列表(list),內含有name(名字)、calories(熱量)、type(類型)屬性。
案例說明:從menu流中,求出菜單中所有菜的熱量和
int calories = menu.stream()
.mapToInt(Dish::getCalories)
.sum();
mapToInt 和 map 對比
首先看兩個源碼
map方法
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
在看其 map 內引用的 Funcation<T,R> 函數式接口內抽象方法 apply
R apply(T t);
推導結論:可以看出如果 menu 內,calories 類型定義 Integer 時,我們案例返回的是 int 類型,這其中會將 Integer 轉化爲 int 類型,這其中就帶出了一個裝箱的成本。
mapToInt方法
IntStream mapToInt(ToIntFunction<? super T> mapper);
在看其 mapToInt 內引用的 ToIntFuncation<T> 函數式接口內抽象方法 applyAsInt
int applyAsInt(T value);
推導結論:可以看出案例中我們需要返回的是 int 類型,並且 mapToInt 返回的也是int類型,這其中是省去了一個裝箱的成本。
2、數值範圍
生成某一範圍的數字流。
加入你想生成 1 到 100 的數字流。
你有兩種方式可以選擇,
第一種:使用 IntStream 和 LongStream 靜態方法,幫助生成這種範圍: range 和 rangeClosed 。這兩個方法都是第一個參數接收起始值,第二個參數結束值。
案例說明:輸出 1 到 100 中所有偶數
案例解決第一步:生成包含 1 到 100 所有整數的流。(用到 IntStream 和 rangeClosed )
案例解決第二步:對省的流進行過濾,過濾出所有的偶數。(用到filter)
案例解決第三步:輸出過濾後的結果(用到 forEach 終端操作 輸出符合條件的結果)
案例代碼實現:
IntStream.rangeClosed(1,100).filter(n -> n % 2 == 0).forEach(System.out::println);
數值流的應用:勾股數
實現思路:
第一步:生成a 爲 1 到 100 範圍內的所有數字。使其生成一個流(通過boxed、和flatMap)
第二步:生成b 爲 1 到 100 範圍內的所有數字。並將 a 、 b 、以及sqrt(a*a+b*b) 生成特化流
第三步:對生成的特化流進行過濾,過濾出 sqrt(a*a+b*b) 爲整數的組合,這裏過濾出的結果集就爲勾股數組。
IntStream.rangeClosed(1,100).boxed()
.flatMap(a1->
IntStream.rangeClosed(1,100).mapToObj(
b -> new double[]{a1,b,Math.sqrt(a1*a1+b*b)})
.filter(t -> t[2] % 1 == 0)).limit(5).forEach(c ->System.out.println(c[0]+","+c[1]+","+c[2]));
上面的案例輸出了0 ~ 100 之間可以組成勾股數的組合
輸出:
3.0,4.0,5.0
4.0,3.0,5.0
5.0,12.0,13.0
6.0,8.0,10.0
7.0,24.0,25.0
知識點解析
解析boxed應用
正確的
A IntStream.range(0, 10).mapToObj(i->new Product()).collect(Collectors.toList());
報錯的
B IntStream.range(0, 10).collect(Collectors.toList());
正確的
C IntStream.range(0,10).boxed().collect(Collectors.toList());
通過上面正確案例和錯誤案例你可以清楚的思考到,可能是因爲裝箱的問題。
一語概括:在Java中,泛型的引用只能綁定到允許引用的對象類型,而(int、byte等)原始類型不屬於對象類型,但是每一個原始類型都是有對應的引用類型,在上方已經對原始類型和引用類型進行了講解。