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?