比较器作为流中的匿名分类器

时间:2016-04-20 03:56:18

标签: java java-stream

让我们说我正在尝试使用特定的比较器对集合进行排序。从性能的角度来看,将sorted()子句中的比较器定义为一个不同的实例是否重要,或者最好只创建一次实例并在sorted()子句中调用compare方法?

从本质上讲,更好的是:

  1. myCollection.stream().sorted(
        new Comparator<String>(){
            public int compare(String a, String b){
                //code
            }
        })
    
  2. Comparator<String> comp = new MyCustomComparator<>();
    myCollection.stream().sorted(comp::compare)
    
  3. 注意:语法和比较值都不重要 - 我想从概念上了解JVM是否足够聪明,只能初始化我的匿名比较器一次(案例1)并继续重用一个方法,或者它将继续创建新实例(然后我会选择案例2)

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。