153. Java Lambda 表达式 - 深入理解 Java Function 接口及其高性能变体

153. Java Lambda 表达式 - 深入理解 Java Function 接口及其高性能变体

Function<T, R> 接口是 Java 中一个功能强大的接口,它用于将类型 T 的对象映射到类型 R 的对象。在流处理、转换和映射操作中,这个接口非常常见。

Function<T, R> 接口的定义

Function<T, R> 接口有一个抽象方法 apply(T t),它接受一个类型为 T 的输入并返回一个类型为 R 的结果:

java 复制代码
@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}

Function<T, R> 在流 API 中用于将元素从一种类型转换为另一种类型,它为数据转换和映射提供了简洁的方式。实际上,Predicate 可以被看作是一个返回 boolean 的专用 Function

示例:使用 Function<String, Integer> 将字符串映射为其长度
java 复制代码
Function<String, Integer> toLength = s -> s.length();
String word = "Hello, world!";
int length = toLength.apply(word);
System.out.println("Length of the word: " + length);

输出:

java 复制代码
Length of the word: 13

在这个示例中,我们定义了一个 Function<String, Integer>,它接受一个字符串并返回该字符串的长度。需要注意,length() 方法返回 int,但是 Function 接口的返回值是 Integer,因此会发生自动装箱和拆箱操作。

如果性能是一个关键问题,避免频繁的装箱和拆箱是很重要的,因此可以考虑使用专用的函数接口。

使用专用的 Function 接口

为了提高性能,JDK 提供了专门的函数接口来处理基本数据类型,避免装箱操作。这些接口包括:

  • IntFunction<R>:接受 int 类型,返回 R 类型的结果。
  • LongFunction<R>:接受 long 类型,返回 R 类型的结果。
  • DoubleFunction<R>:接受 double 类型,返回 R 类型的结果。

这些专用接口避免了装箱操作,提高了性能。

示例:使用 IntFunction<Integer> 将整数转换为字符串的长度
java 复制代码
IntFunction<String> intToString = i -> "Length: " + i;
String result = intToString.apply(42);
System.out.println(result);

输出:

java 复制代码
Length: 42
UnaryOperator<T>:同一类型的转换

UnaryOperator<T>Function<T, T> 的一个特例,表示接受并返回相同类型的转换。它非常适用于对元素执行某些操作而不改变其类型的场景。

例如,所有经典的数学运算符都可以使用 UnaryOperator<T> 来表示,如平方根、三角运算符等。

示例:使用 UnaryOperator<String> 转换字符串为大写
java 复制代码
List<String> strings = Arrays.asList("one", "two", "three");
UnaryOperator<String> toUpperCase = word -> word.toUpperCase();
strings.replaceAll(toUpperCase);
System.out.println(strings);

输出:

java 复制代码
[ONE, TWO, THREE]

在这个示例中,我们使用 UnaryOperator<String> 将列表中的每个字符串转换为大写。注意,replaceAll() 方法会修改列表中的每个元素,但不会改变元素的类型。

BiFunction<T, U, R>:映射两个元素

Function<T, R> 类似,BiFunction<T, U, R> 接口用于处理两个输入参数,并返回一个结果。它的定义如下:

java 复制代码
@FunctionalInterface
public interface BiFunction<T, U, R> {
    R apply(T t, U u);
}

BiFunction 在需要两个参数进行计算时非常有用,例如在计算两个数的和时。

示例:使用 BiFunction<String, String, Integer> 计算单词在句子中的位置
java 复制代码
BiFunction<String, String, Integer> findWordInSentence = (word, sentence) -> sentence.indexOf(word);
String sentence = "This is a test sentence.";
String word = "test";
int position = findWordInSentence.apply(word, sentence);
System.out.println("Position of the word: " + position);

输出:

java 复制代码
Position of the word: 10
BinaryOperator<T>:两个相同类型的操作

BinaryOperator<T>BiFunction<T, T, T> 的特例,它接受两个相同类型的参数并返回相同类型的结果。它通常用于二元运算符,例如加法、减法等。

示例:使用 BinaryOperator<Integer> 计算两个整数的和
java 复制代码
BinaryOperator<Integer> add = (a, b) -> a + b;
int sum = add.apply(5, 3);
System.out.println("Sum: " + sum);

输出:

java 复制代码
Sum: 8
专用版本的 BiFunction

Function 类似,BiFunction 也有针对原始数据类型的专用版本:

  • IntBinaryOperator:接受两个 int 类型的参数并返回一个 int 类型的结果。
  • LongBinaryOperator:接受两个 long 类型的参数并返回一个 long 类型的结果。
  • DoubleBinaryOperator:接受两个 double 类型的参数并返回一个 double 类型的结果。
  • ToIntBiFunction<T, U>:接受两个对象并返回一个 int 类型的结果。
  • ToLongBiFunction<T, U>:接受两个对象并返回一个 long 类型的结果。
  • ToDoubleBiFunction<T, U>:接受两个对象并返回一个 double 类型的结果。

这些专用版本提高了性能,避免了自动装箱。

总结

  • Function<T, R> 接口 :用于将类型 T 的对象映射到类型 R 的对象。它是流 API 中常见的转换工具。
  • UnaryOperator<T>Function<T, T> 的特例,用于处理同一类型的输入和输出。
  • BiFunction<T, U, R>:用于处理两个输入参数的函数。
  • BinaryOperator<T>BiFunction<T, T, T> 的特例,处理两个相同类型的参数。
  • 专用函数接口 :为了提高性能,JDK 提供了多种专用函数接口,如 IntFunctionToIntFunction 等,避免了装箱操作。

通过灵活使用这些函数接口,您可以编写更简洁、可读性更强的代码,特别是在流处理和数据转换中。

相关推荐
前端小巷子几秒前
从双端到快速:Vue 3 Diff 的进化之路
前端·vue.js·面试
玲小珑5 分钟前
LangChain.js 完全开发手册(三)Memory 系统与对话状态管理
前端·langchain·ai编程
用户6120414922139 分钟前
C语言做的量子计算模拟器
c语言·后端·量子计算
pengzhuofan13 分钟前
项目一系列-第7章 父子组件通信
前端·javascript·vue.js
IT_陈寒17 分钟前
Vite 3.0性能飞跃:5个优化技巧让你的构建速度提升200%
前端·人工智能·后端
软测进阶20 分钟前
【第四章】BS 架构测试全解析:从功能验证到问题定位
前端·功能测试·架构·web
小刘的博客间22 分钟前
前端响应式设计
前端·javascript·css
希望201729 分钟前
入门概念|Thymeleaf与Vue
前端·javascript·vue.js
掘金安东尼31 分钟前
JavaScript 接下来要加啥新功能?9个特性!
前端·javascript·github
Victor35635 分钟前
Redis(30)如何手动触发AOF重写?
后端