天天看点

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?