151. Java Lambda 表达式 - 使用 Consumer<T>
接口处理对象
在 Java
中,Consumer<T>
是一个非常重要的功能接口,它接受一个输入参数,但不返回任何结果。与 Supplier<T>
(它不接受参数,返回结果)不同,Consumer<T>
专注于对传入的对象执行操作,而不关心返回值。
Consumer<T>
接口的定义
Consumer<T>
接口只有一个抽象方法 accept(T t)
,用于接受一个类型为 T
的参数并进行处理:
java
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
示例:使用 Consumer<String>
打印字符串
java
Consumer<String> printer = s -> System.out.println(s);
这个 Consumer
实现接收一个字符串并将其打印到控制台。可以像这样使用它:
java
for (int i = 0; i < 5; i++) {
int nextRandom = newRandom.getAsInt();
printer.accept("next random = " + nextRandom);
}
在这个示例中,我们使用 printer
来打印每次生成的随机数。
使用专用的 Consumer
Consumer<T>
接口的实现是通用的,但有时候我们需要处理原始类型数据(例如 int
、long
、double
),这时装箱(autoboxing
)和拆箱(unboxing
)会带来性能开销。为了优化性能,JDK
提供了专门的 Consumer
接口处理原始类型:IntConsumer
、LongConsumer
和 DoubleConsumer
。
示例:使用 IntConsumer
打印整数
java
Consumer<Integer> printer = i -> System.out.println(i);
这段代码使用 Consumer<Integer>
打印整数,但是,如果处理的是大量数据,自动装箱和拆箱可能会影响性能。因此,可以使用专用的 IntConsumer
接口来避免这种性能损失。
java
IntConsumer intPrinter = i -> System.out.println(i);
IntConsumer
的 accept()
方法直接接受 int
类型,而不会进行装箱,因此效率更高。
使用 BiConsumer<T, U>
处理两个参数
除了接受单个参数的 Consumer<T>
,JDK
还提供了 BiConsumer<T, U>
接口,它可以同时接受两个参数,并对它们进行操作。它的定义如下:
java
@FunctionalInterface
public interface BiConsumer<T, U> {
void accept(T t, U u);
}
示例:使用 BiConsumer<Random, Integer>
打印多个随机数
java
BiConsumer<Random, Integer> randomNumberPrinter = (random, number) -> {
for (int i = 0; i < number; i++) {
System.out.println("next random = " + random.nextInt());
}
};
在上面的代码中,randomNumberPrinter
是一个 BiConsumer
,它接受一个 Random
对象和一个整数 number
,并打印出 number
个随机数。
调用这个 BiConsumer
:
java
randomNumberPrinter.accept(new Random(314L), 5);
输出示例:
java
next random = 1
next random = 5
next random = 3
next random = 0
next random = 2
专用的 BiConsumer
接口
与 Consumer<T>
类似,BiConsumer<T, U>
也有专门的版本,处理原始类型数据,避免装箱和拆箱的开销。它们包括:
ObjIntConsumer<T>
:接受一个对象和一个int
。ObjLongConsumer<T>
:接受一个对象和一个long
。ObjDoubleConsumer<T>
:接受一个对象和一个double
。
这些专用接口与 BiConsumer
的工作方式相同,但它们通过避免不必要的装箱来提高性能。
将 Consumer
传递给 Iterable
Iterable
接口的 forEach()
方法是另一个与 Consumer<T>
紧密结合的地方。forEach()
方法接受一个 Consumer<T>
并对 Iterable
中的每个元素执行操作。这使得对集合的处理更加简洁和高效。
示例:使用 forEach()
遍历列表并打印元素
java
List<String> strings = List.of("Java", "Lambda", "Consumer");
Consumer<String> printer = s -> System.out.println(s);
strings.forEach(printer);
在这个例子中,strings.forEach(printer)
会遍历列表 strings
,并将 printer
Consumer
应用于每个元素,将其打印出来。
输出示例:
java
Java
Lambda
Consumer
forEach()
方法提供了一种简单的方式来处理 Iterable
中的每个元素。不再需要手动编写循环语句,代码更加简洁和易于理解。
总结
Consumer<T>
接口 :Consumer<T>
接口用于接受一个对象并对其执行操作,但不返回任何值。它通常用于打印、修改或消费数据。- 专用
Consumer
接口 :为了避免装箱和拆箱带来的性能损失,JDK
提供了专用的IntConsumer
、LongConsumer
和DoubleConsumer
接口处理原始类型。 BiConsumer<T, U>
接口 :BiConsumer
接口允许同时处理两个参数,并执行操作。它在需要处理两个输入时非常有用。forEach()
方法 :forEach()
方法与Consumer<T>
接口结合使用,提供了一种简单的方式来遍历Iterable
并对每个元素执行操作。
通过理解并使用这些接口,可以在 Java
中编写更简洁、易读且高效的代码。