java8 foreach 伟参_Java8 lambda 增强遍历接口 forEach 完全详解

在 Java 7 里,我们遍历集合的时候一般用的是 Iterable 接口 for each 的方式来操作,较之 fori 循环遍历确实已经提高了很大的效率和可读性。

不过应该有不少新手,在迭代使用不当的时候,遇到过 ConcurrentModificationException 异常。

很多人也会在遍历 Map 的时候一筹莫展,计较于哪种方式效率比较高,在哪种情况下使用哪一种迭代方式。或者有人根本就记不住如何迭代 HashMap,每次都要借助搜索引擎。

不过自从 Java 8 进入 lambda 和 default 时代,这些烦恼将不会存在。

java.lang.Iterable.forEach 和 default

在 Java 8 中,接口中的方法可以被实现。

接口中被实现的方法叫做 default 方法,用关键字 default 作为修饰符来标识。

当一个类实现一个接口的时候,它可以实现已经在接口中被实现过的方法,但这不是必须的。这个类会继承 default 方法。这就是为什么当接口发生改变的时候,实现类不需要做改动的原因。

forEach 方法是 Java 8 中在集合父接口 java.lang.Iterable 中新增的一个 default 实现方法:

default void forEach(Consumer super T> action) {

Objects.requireNonNull(action);

for (T t : this) {

action.accept(t);

}

}

5b6ed435585b82362f1160ffe6398812.png

forEach方法接受一个在 Java 8 中新增的 java.util.function.Consumer 的消费行为或者称之为动作(Consumer action )类型;

然后将集合中的每个元素作为消费行为的 accept 方法的参数执行;

直到每个元素都处理完毕或者抛出异常即终止行为;

除非指定了消费行为 action 的实现,否则默认情况下是按迭代里面的元素顺序依次处理。

List 遍历对比

我们通过看一下 java.util.List 的遍历案例,来直观感受一下 Iterable 迭代 迭代和 forEach 遍历的对比。

首先创建一个 ArrayList,存放几个姓名字符串。

List names = new ArrayList<>();

names.add("刘德华");

names.add("张学友");

names.add("黎明");

names.add("郭富城");

Iterable 迭代

简单的把姓名字符串打印出来:

@Test

public void testList()

{

List names = new ArrayList<>();

names.add("刘德华");

names.add("张学友");

names.add("黎明");

names.add("郭富城");

for (String name : names)

{

System.out.println(name);

}

}

执行结果:

刘德华

张学友

黎明

郭富城

forEach 遍历

仍然是打印出姓名字符,执行结果是相同的,看一下代码对比:

@Test

public void testList()

{

List names = new ArrayList<>();

names.add("刘德华");

names.add("张学友");

names.add("黎明");

names.add("郭富城");

//names.forEach(name -> System.out.println(name));

// 或者使用 lambda 表达式的简化版本双冒号表达式

// 调用 out 对象的 println 方法

names.forEach(System.out::println);

}

遍历筛选

假如现在需要把名字字符串为三个汉字的打印出来,又是什么样子的呢?

Iterable 迭代方式:

for (String name : names)

{

if (null != name && name.length() == 3)

{

System.out.println("-- " + name);

}

}

forEach 遍历方式:

names.forEach(name -> {

if (null != name && name.length() == 3)

{

System.out.println("-- " + name);

}

});

或者这样:

// stream and filter

names.stream()

.filter(name -> name != null && name.length() == 3)

.forEach(System.out::println);

Map 遍历对比

Map 接口同样也定义了 forEach 的 default 方法。

基本上,只要是需要或者可以迭代的类,都定义了相似的 forEach default 方法。

default void forEach(BiConsumer super K, ? super V> action) {

Objects.requireNonNull(action);

for (Map.Entry entry : entrySet()) {

K k;

V v;

try {

k = entry.getKey();

v = entry.getValue();

} catch(IllegalStateException ise) {

// this usually means the entry is no longer in the map.

throw new ConcurrentModificationException(ise);

}

action.accept(k, v);

}

}

544fbae40b117dc249dfcac2b8ae4c3e.png

现在看一下 HashMap 的 forEach 遍历是何等方便。

构造数据:

Map items = new HashMap<>();

items.put("A", 10);

items.put("B", 20);

items.put("C", 30);

items.put("D", 40);

items.put("E", 50);

items.put("F", 60);

需求是打印出哈希元素的键值对,如果键为 D, 则多打印出一行 find it!。

Map Iterable 迭代

代码:

@Test

public void testMap()

{

Map items = new HashMap<>();

items.put("A", 10);

items.put("B", 20);

items.put("C", 30);

items.put("D", 40);

items.put("E", 50);

items.put("F", 60);

for (Map.Entry entry : items.entrySet()) {

System.out.println("Item : " + entry.getKey() + " Count : " + entry.getValue());

if("D".equals(entry.getKey())){

System.out.println("find it!");

}

}

}

似曾相识,多么可恶的复杂性,我只是想遍历啊,太复杂了!

Map forEach 遍历

当年,每次在遍历 Map 的时候,都会想,为什么不能像 List 遍历那样,只提供 key value 这样的变量就可以迭代获取到元素呢?现在可以了!

@Test

public void testMap()

{

Map items = new HashMap<>();

items.put("A", 10);

items.put("B", 20);

items.put("C", 30);

items.put("D", 40);

items.put("E", 50);

items.put("F", 60);

items.forEach((k, v) -> {

System.out.println("Item : " + k + " Count : " + v);

if("D".equals(k)){

System.out.println("find it!");

}

});

}

同样的输出,不一样的简洁。

Item : A Count : 10

Item : B Count : 20

Item : C Count : 30

Item : D Count : 40

find it!

Item : E Count : 50

Item : F Count : 60

数组遍历

数组遍历的情况虽然没有那么幸运,得到直接支持,但是我们可以通过转换为 List 的方式间接使用。

String[] data = { "刘德华", "张学友", "黎明", "郭富城", "代码饭" };

List names = Arrays.asList(data);

names.forEach(System.out::println);

自定义消费行为 Consumer

首先,需要实现 java.util.function.Consumer 接口;

再次,实现其 accept 方法

class MyConsumer implements Consumer {

@Override

public void accept(Object t) {

System.out.println("forEach Consumer println:" + t);

}

}

测试,

@Test

public void testForEachConsumer()

{

List myList = new ArrayList();

for(int i=0; i<3; i++) myList.add(i);

MyConsumer myConsumer = new MyConsumer();

myList.forEach(myConsumer);

}

输出:

forEach Consumer println:0

forEach Consumer println:1

forEach Consumer println:2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值