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 ArrayList
s 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 ArrayList
s 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
How to Count the Number of Documents Under a Collection in Firestore
Failed to Resolve Com.Google.Android.Gms Play-Services-Auth:11.4.0
Parsing Query Strings on Android
Must Override a Superclass Method' Errors After Importing a Project into Eclipse
Execution Failed For Task :App:Compiledebugjavawithjavac in Android Studio
Set Imageview Width and Height Programmatically
Difference Between Jsonobject and Jsonarray
Json Array Iteration in Android/Java
Concurrent Modification Exception: Adding to an Arraylist
Why Retrieving Google Directions For Android Using Kml Data Is Not Working Anymore
Base 64 Encode and Decode Example Code
Android/Java - Date Difference in Days
How to Launch an Activity from Another Application in Android
How to Read a Text File in Android
Java Equivalent to PHP'S Preg_Replace_Callback