暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

如何在Java8 Stream中使用索引

编程阁楼 2021-01-16
3149
首先说明下,这是一个很小的知识点,但是却又很实用。
我们来看下使用Java8 Stream迭代时有多爽:
public class Test {


@Test
public void test(){
List<String> list = Arrays.asList("Java", "C#", "Python", "Golang");


list.stream().forEach(x -> {
System.out.println(x);
});
}
}

输出内容:

相比之前的Iterator迭代器或者foreach循环,这种函数式的lambda语法简单且优雅很多,爽的飞起。


接下里问题来了,我们应该如何在这个Stream迭代中使用索引呢?


实际上Stream中是无法直接获取当前变量的索引值的。如果确实有需要,可以改变下思路,使用变相方案进行获取。


方法一:使用索引迭代,通过索引反向获取迭代变量。

看下列代码:

public class Test {


@Test
public void test2(){
List<String> list = Arrays.asList("Java", "C#", "Python", "Golang");


IntStream.range(0, list.size()).forEach(x -> {
System.out.println(x + "==" + list.get(x));
});
}
}

输出结果:

说明:小编非常不喜欢这种方式,因为从语义上来讲,我们迭代的主体并非真实需要迭代的内容,可能存在理解上的误区。

方法二:定义外部变量
public class Test {


@Test
public void test3(){
List<String> list = Arrays.asList("Java", "C#", "Python", "Golang");


int index = 0;
list.stream().forEach(x -> {
System.out.println(index + "==" + x);
index++;
});
}
}
很遗憾,上述代码编译报错:

说明:Java 8语言上的lambda表达式只实现了capture-by-value,也就是说它捕获的局部变量都会拷贝一份到lambda表达式的实体里,然后在lambda表达式里要变也只能变自己的那份拷贝而不影响外部原本的变量;但是Java的设计者为了以后语言能进一步扩展,为支持capture-by-reference留下后路,所以干脆不允许给捕获的变量赋值。
但是Java也只是不允许改变被lambda表达式捕获的变量,并没有限制这些变量所指向的对象的状态能不能变。要从lambda表达式向外传值的常见方法之一就是用长度为1的数组标识迭代索引。

我们修改以上代码如下:
public class Test {


@Test
public void test3(){
List<String> list = Arrays.asList("Java", "C#", "Python", "Golang");


int[] index = {0};
list.stream().forEach(x -> {
System.out.println(index[0] + "==" + x);
index[0]++;
});
}
}
编译已经不报错了,看执行结果也正常了。


方法三:自定义Stream迭代器
public class Test {


public static <E> void forEach(Iterable<? extends E> elements, BiConsumer<Integer, ? super E> action) {
Objects.requireNonNull(elements);
Objects.requireNonNull(action);
int index = 0;
for (E element : elements) {
action.accept(index++, element);
}
}

@Test
public void test4(){
List<String> list = Arrays.asList("Java", "C#", "Python", "Golang");


forEach(list, (index, value) -> {
System.out.println(index + "==" + value);
});
}
}
实现效果:

说明:相对来说,小编更喜欢第三种实现,因为更优雅。同时,小编也疑惑,这么简单且常见的需求,为什么Java语言设计者就不提供实现方案呢?


你更喜欢使用哪种实现呢?


文章转载自编程阁楼,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论