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 等,避免了装箱操作。

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

相关推荐
洛卡卡了26 分钟前
面试官问限流降级,我项目根本没做过,咋办?
后端·面试·架构
ezl1fe1 小时前
RAG 每日一技(十四):化繁为简,统揽全局——用LangChain构建高级RAG流程
人工智能·后端·算法
天才熊猫君1 小时前
npm 和 pnpm 的一些理解
前端
飞飞飞仔1 小时前
从 Cursor AI 到 Claude Code AI:我的辅助编程转型之路
前端
amazingCompass1 小时前
Java 开发必备技能:深入理解与实战 IntelliJ IDEA 中的 VM Options
后端
qb1 小时前
vue3.5.18源码:调试方式
前端·vue.js·架构
欧的曼1 小时前
cygwin+php教程(swoole扩展+redis扩展)
开发语言·redis·后端·mysql·nginx·php·swoole
巴拉巴巴巴拉1 小时前
Spring Boot 整合 Thymeleaf
java·spring boot·后端
Spider_Man2 小时前
缓存策略大乱斗:让你的页面快到飞起!
前端·http·node.js
前端老鹰2 小时前
CSS overscroll-behavior:解决滚动穿透的 “边界控制” 专家
前端·css·html