在 Java 中,为什么 Function.identity() 是静态方法而不是其他方法?
In Java why is Function.identity() a static method instead of something else?
Java 8 个添加的函数式编程构造,包括 Function
class 及其关联的 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),而第二种和第三种实现没有。根据 this SO answer 实际情况并非如此,因此最终这三种方法在性能方面似乎都相当。
使用第二种方法允许将方法用作方法引用,这类似于在函数构造中使用的许多其他标准库方法。例如。 stream.map(Math::abs)
或 stream.map(String::toLowerCase)
.
总的来说,为什么使用第一种方法,看起来(虽然最终不是)性能较差并且与其他示例不同?
TL;DR 使用 Function.identity()
只创建一个对象,因此非常节省内存。
第三个实现无法编译,因为 T
未定义,所以这不是一个选项。
在第二个实现中,每次您编写 Function::identity
都会创建一个 new 对象实例。
在第一个实现中,无论何时调用 Function.identity()
,都会返回 相同 lambda 对象的实例。
自己看很简单。首先在同一个 class 中创建两个 identity
方法,因此将它们重命名为 identity1
和 identity2
以使它们能够单独识别。
static <T> Function<T, T> identity1() {
return t -> t;
}
static <T> T identity2(T in) {
return in;
}
写一个 test
方法接受一个 Function
并打印对象,所以我们可以看到它的唯一身份,如哈希码所反映的那样。
static <A, B> void test(Function<A, B> func) {
System.out.println(func);
}
重复调用test
方法,看是否每一个都得到一个新的对象实例(我的代码在一个名为Test
的class中).
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/0x0000000800ba0840@7adf9f5f
Test$$Lambda/0x0000000800ba0840@7adf9f5f
Test$$Lambda/0x0000000800ba0840@7adf9f5f
Test$$Lambda/0x0000000800ba1040@5674cd4d
Test$$Lambda/0x0000000800ba1440@65b54208
Test$$Lambda/0x0000000800ba1840@6b884d57
Test$$Lambda/0x0000000800ba1840@6b884d57
Test$$Lambda/0x0000000800ba1840@6b884d57
如您所见,多个 statements 调用 Test.identity1()
都得到相同的对象,但是多个 statements 使用 Test::identity2
都得到了不同的对象。
repeated 执行 same 语句确实得到相同的对象(如循环结果所示),但是这与从 different 语句获得的结果不同。
结论:使用Test.identity1()
只创建一个对象,因此比使用Test::identity2
更节省内存.
Java 8 个添加的函数式编程构造,包括 Function
class 及其关联的 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),而第二种和第三种实现没有。根据 this SO answer 实际情况并非如此,因此最终这三种方法在性能方面似乎都相当。
使用第二种方法允许将方法用作方法引用,这类似于在函数构造中使用的许多其他标准库方法。例如。 stream.map(Math::abs)
或 stream.map(String::toLowerCase)
.
总的来说,为什么使用第一种方法,看起来(虽然最终不是)性能较差并且与其他示例不同?
TL;DR 使用 Function.identity()
只创建一个对象,因此非常节省内存。
第三个实现无法编译,因为 T
未定义,所以这不是一个选项。
在第二个实现中,每次您编写 Function::identity
都会创建一个 new 对象实例。
在第一个实现中,无论何时调用 Function.identity()
,都会返回 相同 lambda 对象的实例。
自己看很简单。首先在同一个 class 中创建两个 identity
方法,因此将它们重命名为 identity1
和 identity2
以使它们能够单独识别。
static <T> Function<T, T> identity1() {
return t -> t;
}
static <T> T identity2(T in) {
return in;
}
写一个 test
方法接受一个 Function
并打印对象,所以我们可以看到它的唯一身份,如哈希码所反映的那样。
static <A, B> void test(Function<A, B> func) {
System.out.println(func);
}
重复调用test
方法,看是否每一个都得到一个新的对象实例(我的代码在一个名为Test
的class中).
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/0x0000000800ba0840@7adf9f5f
Test$$Lambda/0x0000000800ba0840@7adf9f5f
Test$$Lambda/0x0000000800ba0840@7adf9f5f
Test$$Lambda/0x0000000800ba1040@5674cd4d
Test$$Lambda/0x0000000800ba1440@65b54208
Test$$Lambda/0x0000000800ba1840@6b884d57
Test$$Lambda/0x0000000800ba1840@6b884d57
Test$$Lambda/0x0000000800ba1840@6b884d57
如您所见,多个 statements 调用 Test.identity1()
都得到相同的对象,但是多个 statements 使用 Test::identity2
都得到了不同的对象。
repeated 执行 same 语句确实得到相同的对象(如循环结果所示),但是这与从 different 语句获得的结果不同。
结论:使用Test.identity1()
只创建一个对象,因此比使用Test::identity2
更节省内存.