Efficiency of Java "Double Brace Initialization"

Efficiency of Java Double Brace Initialization?

Here's the problem when I get too carried away with anonymous inner classes:

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

These are all classes which were generated when I was making a simple application, and used copious amounts of anonymous inner classes -- each class will be compiled into a separate class file.

The "double brace initialization", as already mentioned, is an anonymous inner class with an instance initialization block, which means that a new class is created for each "initialization", all for the purpose of usually making a single object.

Considering that the Java Virtual Machine will need to read all those classes when using them, that can lead to some time in the bytecode verfication process and such. Not to mention the increase in the needed disk space in order to store all those class files.

It seems as if there is a bit of overhead when utilizing double-brace initialization, so it's probably not such a good idea to go too overboard with it. But as Eddie has noted in the comments, it's not possible to be absolutely sure of the impact.


Just for reference, double brace initialization is the following:

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

It looks like a "hidden" feature of Java, but it is just a rewrite of:

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

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

So it's basically a instance initialization block that is part of an anonymous inner class.


Joshua Bloch's Collection Literals proposal for Project Coin was along the lines of:

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

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

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

Sadly, it didn't make its way into neither Java 7 nor 8 and was shelved indefinitely.


Experiment

Here's the simple experiment I've tested -- make 1000 ArrayLists with the elements "Hello" and "World!" added to them via the add method, using the two methods:

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!");

I created a simple program to write out a Java source file to perform 1000 initializations using the two methods:

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);
}
}

Please note, that the elapsed time to initialize the 1000 ArrayLists and the 1000 anonymous inner classes extending ArrayList is checked using the System.currentTimeMillis, so the timer does not have a very high resolution. On my Windows system, the resolution is around 15-16 milliseconds.

The results for 10 runs of the two tests were the following:

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

As can be seen, the double brace initialization has a noticeable execution time of around 190 ms.

Meanwhile, the ArrayList initialization execution time came out to be 0 ms. Of course, the timer resolution should be taken into account, but it is likely to be under 15 ms.

So, there seems to be a noticeable difference in the execution time of the two methods. It does appear that there is indeed some overhead in the two initialization methods.

And yes, there were 1000 .class files generated by compiling the Test1 double brace initialization test program.

What is Double Brace initialization in Java?

Double brace initialisation creates an anonymous class derived from the specified class (the outer braces), and provides an initialiser block within that class (the inner braces). e.g.

new ArrayList<Integer>() {{
add(1);
add(2);
}};

Note that an effect of using this double brace initialisation is that you're creating anonymous inner classes. The created class has an implicit this pointer to the surrounding outer class. Whilst not normally a problem, it can cause grief in some circumstances e.g. when serialising or garbage collecting, and it's worth being aware of this.

Double brace initialization - Advantage

not advised to use it because of its performance issue.

I don't see any performance issue. Whenever you see someone say I did/didn't do something for performance reason you should expect to see detailed analysis comparing the alternatives explaining how one met the specific required performance and the other did not. If you don't see all this you might assume the author is just guessing.

EDIT: While I will concede each class takes a small amount of time to load, the running performance is exactly the same. I have demonstrated here https://stackoverflow.com/a/14627268/57695

I would use double brace notation if you believe it's simpler and clearer.

A disadvantage is that your are changing the type of the collection which can confuse functions where that is not expected. e.g. equals.

Note: As Lukas Eder points out, you want to be careful if you do this in a non-static context. The anonymous sub-class collection will implicitly have a reference to the outer instance and if it lives longer than the collection, this would be a memory leak. Have you ever thought of the possibility of a memory leak?

Java double brace initialization vs lambda

Lambda expressions are different from an anonymous inner class that happens to implement a functional interface.

Anonymous inner classes will create their own class file at compilation, usually something along the lines of Foo$1.class, if it's contained in the Foo class. It is a fully functional class that implements an interface or subclasses a class. To reference local values outside its scope, it will, behind the scenes, create an instance variable in the anonymous inner class that represents a copy of the value. This is why the variable must be effectively final -- otherwise the actual variable may change and the copy may be stale.

Lambda expressions don't create anonymous inner classes. They use a java.lang.invoke.LambdaMetafactory that produces a CallSite that can be used later to execute the lambda expression. The lambda expression, whether it's a block or an expression, gets converted to a hidden private static method within the class in which it's contained. Instead of creating a class with a hidden variable, captured values get translated into parameters of the hidden private static method. Local values still must be effectively final because the value passed to the method is again a copy. The method gets invoked by a invokedynamic instruction in the JVM.

Sources:

  • How Lambdas And Anonymous Inner Classes Work
  • PART 4 – HOW LAMBDA EXPRESSION INTERNALLY WORKS

Java double brace initialization works always?

There is a detail you left out that explains this.

First of all, let's review steps 3 through 5 of the initialization procedure (summarized):

3. the superclass constructor is called

4. the instance initializers are called

5. the body of the constructor is called

The detail that you've left out is that your expression is not simply creating a new instance of the HashSet class, it is in fact creating a new instance of an anonymous subclass of HashSet. (I believe this is specified in section 15.9.1.)

Since you did not declare a constructor, the default constructor is used. But before that, the constructor of the superclass HashSet has completed.

So, in summary, the HashSet constructor completes before your initializer block runs.

Double Brace Initialization in java

People come up with great new ways to solve problems in Java all the time. But there are only few places where you can actually find compilations of such tricks.

A good place to start is to use frameworks like Spring and Guava because they contains state-of-the-art tricks that will make your life much easier (and your code smaller and safer after a while).

Java Double brace initialization

If the base class implements java.io.Serializable then subclasses will should have a serialVersionUID. Inner classes should not be serialisable.

If you are planning objects created by this code to be exposed to other code that potentially might want to serialise the data, don't use the double brace idiom (I suppose you could use it with writeReplace, but that gets a bit ugly). If you are sure your code isn't going to come into contact with serilisation, stick @SuppressWarnings("serial") on the widest possible context.

Alternative to double brace initialization

Instead of:

new ArrayList<String>() {{ add("Y"); add("Upload Success"); }}

you may use:

Arrays.asList("Y", "Upload Success")

This gives you a fixed-size list. If you want to be able to add or remove elements later, convert it into an ArrayList:

new ArrayList<>(Arrays.asList("Y", "Upload Success"))

And of course you can put this list into its own variable before putting it into your map structure.

If you want to put either [Y, Upload Success] or [N, Upload Failed] and make sure the lists aren’t shared between map entries, here’s a suggestion: First, outside your loop:

final List<String> successList = Arrays.asList("Y", "Upload Success");
final List<String> failureList = Arrays.asList("N", "Upload Failed");

Then inside your loop:

if (wasSuccessful) {
errorList.put(tempName,
Collections.singletonMap("upl", new ArrayList<>(successList)));
} else {
errorList.put(tempName,
Collections.singletonMap("upl", new ArrayList<>(failureList)));
}

You may take the idea one step further and build the maps outside the loop. And again, if you want the inner map to be a HashMap, just convert into one: new HashMap<>(Collections.singletonMap("upl", new ArrayList<>(successList))).

You notice I have avoided the double brace initialization completely. While it’s brief, it has an overhead both conceptually and performancewise. You are creating a new anonymous subclass each time, which I don’t find warranted.



Related Topics



Leave a reply



Submit