1. 問題來源
Java8的Stream流為我們的周遊集合帶來了友善,基本可以取代for循環了。但是有一些情況需要知道目前周遊的索引,使用for循環當然可以輕易獲得,但使用stream就很難了。
比如下面這個情況:
有一個集合list,裡面存儲的是引用類型。
使用for循環可以輕易的操作索引i
for (int i = 0; i < list.size(); i++){
System.out.println(list.get(i));
System.out.println(i);
}
使用Stream流周遊list如下,其中handle是一個方法,想在handle方法裡面拿到目前索引是很困難的。
list.stream().map(t -> handle(t)).collect(Collectors.toList());
2. 解決辦法
使用IntStream流來構造一個Int類型的流出來,然後周遊這個Int的流,list中的對象可以通過get方法來取。具體解決代碼如下:
IntStream.range(0, lists.size())
.mapToObj(i -> handle(lists.get(i), i))
.collect(Collectors.toList());
可以看到代碼裡的這一句:handle(lists.get(i), i),這樣就成功的把索引帶入到了handle方法中。
需要注意的是:在流中必須使用mapToObj,而不能使用map映射
3. map映射和mapToObj的差別
首先Stream流下面的類包含了IntStream, LongStream, DoubleStream等
- 那麼究竟下面兩者有什麼差別呢?
Stream<Integer> // 包裝類型
IntStream //基本類型
是以對于mapToObj和mapToInt也是同樣的
mapToObj 方法主要是将Stream中的元素進行裝箱操作, 轉換成一個引用類型的值。
mapToInt 方法是将Stream中的 元素轉換成基本類型int。
比如下面的例子
Stream s = IntStream.of(4, 5, 6).mapToObj(e -> e); //mapToObj method is needed
IntStream is = Stream.of(4, 5, 6).mapToInt(e -> e); //mapToInt method is needed
可以看到Stream是包裝類型,是以想要把IntStream基本類型流轉化成包裝類型,就需要使用mapToObj。
-
上面兩個mapToObj和mapToInt是進行類型的轉化,那麼map的作用呢?
map不進行類型轉化,如果原來流中是基本類型,map映射完應當還是基本類型,如果原來是包裝類型,映射完應當還是包裝類型。
比如下面這個例子:
IntStream.of(1, 2, 3, 4, 5, 6, 7).map(elem -> elem * 10).forEach(System.out::println);
這也就解釋了,為什麼上面的第二節解決辦法裡面,用map不行,而需要mapToObj,因為那裡做了一個基本類型到包裝類型的轉化
3. 總結
- 使用IntStream可以靈活的操作對象和擷取索引。
- map不進行包裝和基本類型的轉化,mapToObj是基本轉為包裝,mapToInt是包裝轉為基本。
END
參考
Java 8之基本類型優化
Java Stream difference between map and mapToObj
How to get element index when using a stream to traverse a list?