在Java中,为什么Function.identity()是一个静态方法而不是其他方法?



Java 8添加了函数式编程结构,包括Function类及其关联的identity()方法。

这是此方法的当前结构:

// Current implementation of this function in the [JDK source][1]
static <T> Function<T, T> identity() {
return t -> t;
}
// Can be used like this
List<T> sameList = list.stream().map(Function.identity()).collect(Collectors.toList());

但是,还有第二种方法来构建它:

// Alternative implementation of the method
static <T> T identity(T in) {
return in;
}
// Can be used like this
List<T> sameList = list.stream().map(Function::identity).collect(Collectors.toList());

甚至还有第三种方法来构建它:

// Third implementation
static final Function<T, T> IDENTITY_FUNCTION = t -> t;
// Can be used like this
List<T> sameList = list.stream().map(Function.IDENTITY_FUNCTION).collect(Collectors.toList());

在这三种方法中,JDK中实际使用的第一种方法看起来内存效率较低,因为它似乎在每次使用时都会创建一个新对象(lambda(,而第二种和第三种实现则没有。根据这个SO答案,实际情况并非如此,因此最终所有三种方法在性能方面似乎相对相同。

使用第二种方法允许将该方法用作方法引用,这类似于函数构造中使用的许多其他标准库方法。 例如stream.map(Math::abs)stream.map(String::toLowerCase).

总的来说,为什么要使用第一种方法,这种方法看起来(尽管最终不是(性能较差,并且与其他示例不同?

TL;DR 使用Function.identity()只创建一个对象,因此非常节省内存。


第三个实现不编译,因为T未定义,所以这不是一个选项。

在第二个实现中,每次编写Function::identity都会创建一个新的对象实例。

在第一次实现中,每当您调用Function.identity()时,都会返回同一lambda 对象的实例。

亲眼看看很简单。首先在同一类中创建两个identity方法,因此将它们重命名为identity1identity2以使它们单独标识。

static <T> Function<T, T> identity1() {
return t -> t;
}
static <T> T identity2(T in) {
return in;
}

编写一个接受Function并打印对象的test方法,以便我们可以看到它的唯一标识,如哈希代码所反映的那样。

static <A, B> void test(Function<A, B> func) {
System.out.println(func);
}

反复调用test方法以查看每个方法是否获得一个新的对象实例(我的代码位于名为Test的类中(。

test(Test.identity1());
test(Test.identity1());
test(Test.identity1());
test(Test::identity2);
test(Test::identity2);
for (int i = 0; i < 3; i++)
test(Test::identity2);

输出

Test$$Lambda$1/0x0000000800ba0840@7adf9f5f
Test$$Lambda$1/0x0000000800ba0840@7adf9f5f
Test$$Lambda$1/0x0000000800ba0840@7adf9f5f
Test$$Lambda$2/0x0000000800ba1040@5674cd4d
Test$$Lambda$3/0x0000000800ba1440@65b54208
Test$$Lambda$4/0x0000000800ba1840@6b884d57
Test$$Lambda$4/0x0000000800ba1840@6b884d57
Test$$Lambda$4/0x0000000800ba1840@6b884d57

如您所见,调用Test.identity1()的多个语句都获取相同的对象,但使用Test::identity2的多个语句都获取不同的对象。

确实,重复执行同一语句会得到相同的对象(如循环的结果所示(,但这与从不同语句获得的结果不同。

结论:使用Test.identity1()只会创建一个对象,因此它比使用Test::identity2更节省内存。

最新更新