网站首页 > java教程 正文
在之前的文章,到目前为止我们已经看到了三个功能接口,Consumer,BiConsumer和Predicate。没看过的同学可以先了解一下。
在本文中,我们将了解另一个函数式接口Function。
Function是函数式编程中另一个值得关注的重要接口。顾名思义,它代表一个应用于给定输入并返回结果的函数。接口定义如下所示。
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(
Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(
Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}
可以看到抽象方法是R apply(T t)。接受我们有两个类型参数:T 和 R。
T:是输入参数的类型
R:是返回值的类型。
这里提到一个重要注意事项是Function 与 Consumer 完全相同,只是它返回一个值,而 Consumer 不返回值。
什么时候使用
有很多地方可以使用,这里了解两种情况
1、使用 Stream 对象上的 map() 方法。
2、使用map对象的computeIfAbsent()方法。
在Stream对象上使用map()
假设我们有一个代表整数值的 String 对象流,并且我们希望将它们转换为整数。所以函数的输入是一个字符串,输出是一个整数。以下是我们如何实现它。
Function<String, Integer> atoi = new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.valueOf(s);
}
};
将上面的匿名内部类转换为 lambda 我们得到了这个。
Function<String, Integer> atoi = str -> Integer.valueOf(str);
这是表示整数的字符串流。
Stream<String> numberStrs =
Stream.of("11", "111", "11111", "1111111");
现在,在这个流对象上,我们将这个函数 atoi 与下面的 map() 方法一起使用
Stream<Integer> numbers = numberStrs.map(atoi);
我们甚至可以将其放在一行中,如下所示。
Stream<Integer> numbers = numberStrs.map(
str -> Integer.valueOf(str));
map() 方法将函数 atoi 应用于流中的每个对象并返回一个新流。所以我们在这里所做的是将字符串对象流转换为整数对象流。
我们可以根据需要连接任意多个 map() 方法来应用不同的转换。例如,将字符串转换为整数后,如果我们想要将平方作为长整数返回,则可以按如下方式执行。
Stream<Long> longNumbers = numberStrs
.map(s -> Integer.valueOf(s))
.map(i -> i * i)
.map(l -> Long.valueOf(l));
map() 方法还有其他变体,采用 Function 接口的其他变体。
Map 对象上使用computeIfAbsent()
这是Function 实现 lambda 的第二个地方。想要实现的功能是,当该key的数据在map不存在时才将数据添加进去
假设我们有一个字符串及其长度的键值对,如下所示。
Map<String, Integer> numbersMap = new HashMap<>(Map.of(
"Two", "Two".length(),
"THREE", "THREE".length(),
"FOUR", "FOUR".length(),
"FIVE", "FIVE".length()
));
现在,仅当没有具有给定key的数据时,才添加到map。在这种情况下,我们可以使用computeIfAbsent()方法并给出实现Function接口的lambda。
numbersMap.computeIfAbsent("SIX", v -> v.length());
这里的key是 SIX,map中不存在。因此,上面的代码将在map中添加一个数据,其中 SIX为key,3 为值。
“Function”的变体
Function 接口有多种的变体,如下所示。
- ToIntFunction:接受参数 T,并返回整数值。
- ToLongFunction:接受参数 T,并返回一个 long 值。
- ToDoubleFunction:采用参数 T,并返回double值。
- BiFunction:采用两个参数 T 和 U,并返回 R。
这些函数分别与 IntStream、LongStream、DoubleStream 和 Stream 类一起使用,它们各自的映射方法是 mapToInt()、mapToLong() 和 mapToDouble()。
LongStream longs = numberStrs
.map(str -> Integer.valueOf(str))
.mapToLong(i -> i);
或者我们可以简单地按照下面的方法做,这比上面的方法更好。
LongStream longs = numberStrs.mapToLong(str -> Long.valueOf(str));
除了我们下面讨论的 BiFunction 之外,所有其他变体都是相通的。相信大家都能看明白
BiFunction接口
BiFunction 与 BiConsumer 相同,只是它返回一个值。因此 BiFunction 接受两个参数并返回一个值。接口定义如下所示。
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
default <V> BiFunction<T, U, V> andThen(
Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t, U u) -> after.apply(apply(t, u));
}
}
它有抽象方法 apply() 和默认方法 andThen()。
BiFunction 共有 3 个类型参数。 T和U是输入类型参数,R是返回值类型。
哪里使用BiFunction
每当我们想要将两个值转换为另一个值或处理像map这样的两个值对象的数据时,我们可以使用 BiFunction。
BiFunction<Integer, Integer, Double> exponent =
(x, y) -> Math.pow(x, y);
我们在这里所说的类型参数是指有两个输入参数,它们是整数,结果是双精度值。
我们也可以在 Map 对象上使用它。假设我们有一个以字符串为key、以整数为value的map,如下所示,我们可以使用 BiFunction 来转换map的所有值。
例如,我们有下面的map。
Map<String, Integer> numbersMap = new HashMap<>(Map.of(
"Two", 2,
"THREE", 3,
"FOUR", 4,
"FIVE", 5
));
将 BiFunction 与 ReplaceAll() 方法结合使用
现在我们希望map的每个值都计算平方。如果没有 lambda,迭代所有值并将它们再次放入map将会很痛苦。这里replaceAll()就派上用场了。
ReplaceAll() 的作用是,它获取 BiFunction,将其应用于与特定key对应的每个value,获取应用 BiFunction 的结果的新value,并替换该key的新value。我们是这样做的。
System.out.println("前: " + numbersMap);
numbersMap.replaceAll((k, v) -> v * v);
System.out.println("后: " + numbersMap);
输出
前: {FIVE=5, Two=2, FOUR=4, THREE=3}
后: {FIVE=25, Two=4, FOUR=16, THREE=9}
让我们来分解一下。我们在这里使用的 lambda 是 (k, v) -> v * v 。参数 k 和 v 是输入,它们是字符串和整数。返回值为 Integer,即 v * v
如果你不理解 lambda,只需先简单地编写匿名内部类,然后将其转换为 lambda。这里我来写一下匿名内部类。
numbersMap.replaceAll(new BiFunction<String, Integer, Integer>() {
@Override
public Integer apply(String k, Integer v) {
return v * v;
}
});
将 BiFunction 与computeIfPresent() 结合使用
例如,我们想要更改现有key的value。我们可以按如下方式执行此操作。
Map<String, Integer> numbersMap = new HashMap<>(Map.of(
"Two", 2,
"THREE", 3,
"FOUR", 4,
"FIVE", 5
));
numbersMap.replaceAll((k, v) -> v * v);
numbersMap.computeIfPresent("FOUR", (k,v) -> k.length())
输出
{FIVE=25, Two=4, THREE=9, FOUR=4}
上面就是Function的所有基础知识。函数有很多变体,例如IntFunction、ToIntFunction、LongFunction等等ToLongFunction。我们需要查看Java Docs注释来选择适合我们的。但问题是,如果我们处理像 int、long 和 double 这样的基本类型,最好使用IntFunction、ToIntFunction和基本类型的其他变体,因为它们有更好的性能,因为不涉及自动装箱。
概括
- Function类似,Consumer但唯一的区别是Function返回值,Consumer不返回值。
- 我们使用 Function withcomputeIfAbsent()和BiFunctionwith computeIfPresent()。
- 尽量使用函数的基本类型变体总,因为避免自动装箱,并且可以以更少的内存占用提供更好的性能。
猜你喜欢
- 2024-10-07 python函数式编程(python函数经典例程)
- 2024-10-07 JavaScript 的函数式编程与面向对象编程区别在哪?
- 2024-10-07 函数式编程:Python和Java的实现方式有哪些?
- 2024-10-07 函数式编程与命令式编程的区别以及函数式编程不需要锁
- 2024-10-07 前端同学如何函数式编程?(前端函数式编程和响应式编程)
- 2024-10-07 Java函数式编程实战:Consumer、Predicate和Supplier的使用指南
- 2024-10-07 java码农福音:函数式编程和lambda表达式
- 2024-10-07 深入理解Java中的Lambda表达式和函数式编程的关系
- 2024-10-07 Java 的函数式接口(必懂知识点!)(jdk1.8函数式接口)
- 2024-10-07 Java新特性之-函数式编程(函数式 java)
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)