龙空技术网

java8精华-函数式编程-Function (五)

代码小人物 344

前言:

如今各位老铁们对“java中取整函数”大致比较珍视,看官们都需要了解一些“java中取整函数”的相关内容。那么小编在网上搜集了一些关于“java中取整函数””的相关内容,希望大家能喜欢,小伙伴们快快来了解一下吧!

在之前的文章,到目前为止我们已经看到了三个功能接口,ConsumerBiConsumerPredicate。没看过的同学可以先了解一下。

Java8精华-函数式编程(一)读完这篇,你将彻底理解

Java8精华-函数式编程-Consumer(二)

java8精华-函数式编程-BiConsumer(三)

java8精华-函数式编程-Predicate(四)

在本文中,我们将了解另一个函数式接口Function

Function是函数式编程中另一个值得关注的重要接口。顾名思义,它代表一个应用于给定输入并返回结果的函数。接口定义如下所示。

@FunctionalInterfacepublic 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 接受两个参数并返回一个值。接口定义如下所示。

@FunctionalInterfacepublic 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的所有基础知识。函数有很多变体,例如IntFunctionToIntFunctionLongFunction等等ToLongFunction。我们需要查看Java Docs注释来选择适合我们的。但问题是,如果我们处理像 int、long 和 double 这样的基本类型,最好使用IntFunctionToIntFunction和基本类型的其他变体,因为它们有更好的性能,因为不涉及自动装箱。

概括Function类似,Consumer但唯一的区别是Function返回值,Consumer不返回值。我们使用 Function withcomputeIfAbsent()BiFunctionwith computeIfPresent()尽量使用函数的基本类型变体总,因为避免自动装箱,并且可以以更少的内存占用提供更好的性能。

标签: #java中取整函数