天天看點

Java8 Stream流周遊 如何使用索引

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等

  1. 那麼究竟下面兩者有什麼差別呢?
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。

  1. 上面兩個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. 總結

  1. 使用IntStream可以靈活的操作對象和擷取索引。
  2. 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?