让我们说我正在尝试使用特定的比较器对集合进行排序。从性能的角度来看,将sorted()子句中的比较器定义为一个不同的实例是否重要,或者最好只创建一次实例并在sorted()子句中调用compare方法?
从本质上讲,更好的是:
myCollection.stream().sorted(
new Comparator<String>(){
public int compare(String a, String b){
//code
}
})
Comparator<String> comp = new MyCustomComparator<>();
myCollection.stream().sorted(comp::compare)
注意:语法和比较值都不重要 - 我想从概念上了解JVM是否足够聪明,只能初始化我的匿名比较器一次(案例1)并继续重用一个方法,或者它将继续创建新实例(然后我会选择案例2)
答案 0 :(得分:1)
每次评估使用new
的表达式时,都会创建一个新的匿名类实例。
在您的第一个示例中,每次语句运行时都会创建一个新的示例,并将其传递给sorted
。
在第二个示例中,在comp
变量初始化的任何位置创建一个新的示例。如果comp
是实例成员,则只要创建拥有它的对象,就会创建它。如果comp
是方法中的局部变量,则每次调用该方法时都会创建它。
static
,无状态和非捕获Comparator
始终是最有效的方式,因为您可以创建一次并永久保留它。 (例如参见String.CASE_INSENSITIVE_ORDER
。)
这并不是说你不应该用另一种方式。
在Java 8中,您应该优先使用lambdas而不是匿名类。非捕获lambda可以缓存,只创建一次。例如,该程序输出true
:
class Example {
public static void main(String[] args) {
System.out.println(comparator() == comparator());
}
static Comparator<String> comparator() {
return (lhs, rhs) -> lhs.compareTo(rhs);
}
}
(Ideone上的示例。)
所有这一切,你不应该担心在Java中创建一些小对象,因为它是不可避免的,并且垃圾收集器已针对它进行了优化。绝大多数时候,“最好”的做事方式也是最具可读性的。
请注意,您不必在第二个示例中使用方法引用。您可以直接将其传递给方法:
Comparator<String> comp = new MyCustomComparator<>();
myCollection.stream().sorted(comp)...
答案 1 :(得分:0)
两种方法的运行时都是一样的。这可以表示如下:
对于1st scenario
,第一个JVM使用您的自定义代码比较方法创建比较器实例,并匿名为此对象分配空间。因此,最终创建对象并为其分配内存而没有为用户提供一些指向引用,并且一旦为GC注册了函数调用over对象。
对于2nd scenario
,JVM再次创建了具有自定义代码和分配空间的比较器的新实例,并且还提供了存储在单独变量中的引用,以便可以再次使用此对象,但此处对象不会被收集如果在其他任何地方的代码中再次使用GC。因此,当GC下次运行时,它必须扫描变量的引用并确定它是否可以被GC。