Java“双支架初始化”的效率?

问题:

Hidden Features of Java顶部回答提及Double Brace Initialization中,采用very诱惑语法:

Set<String> flavors = new HashSet<String>() {{
    add("vanilla");
    add("strawberry");
    add("chocolate");
    add("butter pecan");
}};

这个成语创建了一个匿名内部类,其中只有一个实例初始化器,它可以在包含范围中使用任何[…]方法。
主要问题:这是否为低效呢?它的使用应该限于一次性初始化? (当然炫耀!)
第二个问题:新的HashSet必须是实例初始化程序中使用的“this”…任何人都可以了解机制?
第三个问题:这个成语也是朦胧用于生产代码?
 概要:非常非常好的答案,谢谢大家。在问题(3)中,人们觉得语法应该是清楚的(尽管我建议偶尔发表评论,特别是如果您的代码会传递给可能不熟悉的开发人员)。
在问题(1)中,生成的代码应该很快运行。额外的.class文件确实导致jar文件混乱,并且程序启动缓慢(感谢@coobird进行测量)。 @Thilo指出,垃圾收集可能会受到影响,而在某些情况下,额外加载类的内存成本可能是一个因素。
问题(2)原来是对我最有趣的。如果我明白答案,DBI发生的是匿名的内部类扩展了由new操作符构造的对象的类,因此引用了一个引用正在构造的实例的“this”值。井井有条。
总的来说,DBI会把我当作一种好奇心的事情。 Coobird等人指出,您可以通过Arrays.asList,varargs方法,Google Collections和所提出的Java 7 Collection文字来实现相同的效果。较新的JVM语言(如Scala,JRuby和Groovy)也为列表构建提供简洁的符号,并与Java进行良好的互操作。鉴于DBI将类路径整合起来,减慢了类加载速度,并使代码更加模糊,我可能会避开它。不过,我打算在一个刚刚获得SCJP的朋友,并喜欢关于Java语义的好主意的朋友们的春天! ;-) 感谢大家!
7/2017:双重括号初始化的Baeldung has a good summary,并将其视为反模式。

回答:

当我遇到匿名的内部类时,这是一个问题:

2009/05/27  16:35             1,602 DemoApp2$1.class
2009/05/27  16:35             1,976 DemoApp2$10.class
2009/05/27  16:35             1,919 DemoApp2$11.class
2009/05/27  16:35             2,404 DemoApp2$12.class
2009/05/27  16:35             1,197 DemoApp2$13.class

/* snip */

2009/05/27  16:35             1,953 DemoApp2$30.class
2009/05/27  16:35             1,910 DemoApp2$31.class
2009/05/27  16:35             2,007 DemoApp2$32.class
2009/05/27  16:35               926 DemoApp2$33$1$1.class
2009/05/27  16:35             4,104 DemoApp2$33$1.class
2009/05/27  16:35             2,849 DemoApp2$33.class
2009/05/27  16:35               926 DemoApp2$34$1$1.class
2009/05/27  16:35             4,234 DemoApp2$34$1.class
2009/05/27  16:35             2,849 DemoApp2$34.class

/* snip */

2009/05/27  16:35               614 DemoApp2$40.class
2009/05/27  16:35             2,344 DemoApp2$5.class
2009/05/27  16:35             1,551 DemoApp2$6.class
2009/05/27  16:35             1,604 DemoApp2$7.class
2009/05/27  16:35             1,809 DemoApp2$8.class
2009/05/27  16:35             2,022 DemoApp2$9.class

这些都是在我做一个简单的应用程序时生成的所有类,并且使用大量的匿名内部类 – 每个类将被编译成单独的class文件。
如前所述,“双括号初始化”是一个具有实例初始化块的匿名内部类,这意味着为每个“初始化”创建一个新类,所有这些都是为了通常制作单个对象。
考虑到Java虚拟机在使用它们时需要读取所有这些类,这可能会在bytecode verfication过程中导致一段时间。更不用说增加所需的磁盘空间,以便存储所有这些class文件。
在使用双大括号初始化时,似乎有一点开销,所以可能不是太过分了。但是如Eddie在评论中指出的那样,不可能绝对确定影响。
仅供参考,双括号初始化如下:

List<String> list = new ArrayList<String>() {{
    add("Hello");
    add("World!");
}};

它看起来像Java的“隐藏”功能,但它只是一个重写:

List<String> list = new ArrayList<String>() {

    // Instance initialization block
    {
        add("Hello");
        add("World!");
    }
};

所以这基本上是instance initialization blockanonymous inner class的一部分
Joshua Bloch的Collection Literals proposal Project Coin符合以下条件:

List<Integer> intList = [1, 2, 3, 4];

Set<String> strSet = {"Apple", "Banana", "Cactus"};

Map<String, Integer> truthMap = { "answer" : 42 };

可悲的是,它不属于Java 7和8,并被无限期搁置。
 实验
这是我测试过的简单实验 – 使用add方法添加元素"Hello""World!",使用以下两种方法使1000 ArrayList
 Method 1: Double Brace Initialization

List<String> l = new ArrayList<String>() {{
  add("Hello");
  add("World!");
}};

 Method 2: Instantiate an ArrayList and add

List<String> l = new ArrayList<String>();
l.add("Hello");
l.add("World!");

我创建了一个简单的程序来编写一个Java源文件,使用两种方法执行1000个初始化:
 Test 1:

class Test1 {
  public static void main(String[] s) {
    long st = System.currentTimeMillis();

    List<String> l0 = new ArrayList<String>() {{
      add("Hello");
      add("World!");
    }};

    List<String> l1 = new ArrayList<String>() {{
      add("Hello");
      add("World!");
    }};

    /* snip */

    List<String> l999 = new ArrayList<String>() {{
      add("Hello");
      add("World!");
    }};

    System.out.println(System.currentTimeMillis() - st);
  }
}

 Test 2:

class Test2 {
  public static void main(String[] s) {
    long st = System.currentTimeMillis();

    List<String> l0 = new ArrayList<String>();
    l0.add("Hello");
    l0.add("World!");

    List<String> l1 = new ArrayList<String>();
    l1.add("Hello");
    l1.add("World!");

    /* snip */

    List<String> l999 = new ArrayList<String>();
    l999.add("Hello");
    l999.add("World!");

    System.out.println(System.currentTimeMillis() - st);
  }
}

请注意,使用System.currentTimeMillis检查初始化1000 ArrayList和1000个匿名内部类延长的时间,因此定时器的分辨率不高。在我的Windows系统上,分辨率大约在15-16毫秒。
两次测试的10次运行结果如下:

Test1 Times (ms)           Test2 Times (ms)
----------------           ----------------
           187                          0
           203                          0
           203                          0
           188                          0
           188                          0
           187                          0
           203                          0
           188                          0
           188                          0
           203                          0

可以看出,双支架初始化具有大约190ms的明显的执行时间。
同时,ArrayList初始化执行时间为0 ms。当然,定时器分辨率应该被考虑在内,但可能在15ms以下。
因此,两种方法的执行时间似乎有明显差异。在两种初始化方法中确实看起来确实有一些开销。
是的,通过编译Test1双大括号初始化测试程序生成了1000个.class文件。

 
 
Code问答: http://codewenda.com/topics/python/
Stackoverflow: Efficiency of Java “Double Brace Initialization”?

*转载请注明本文链接以及stackoverflow的英文链接

发表评论

电子邮件地址不会被公开。 必填项已用*标注

− 3 = 4