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.
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.
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
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.
How do I fix these double braces
To understand how to fix it, you should first understand what it's doing. For this, you'll need to know about two things:
- Anonymous sub-classes
- Instance initializer blocks
The TL;DR of how to fix it is just to split those put
calls and initialization out from the setter:
Map<String, String> valueMap = new LinkedHashMap<String, String>();
valueMap.put("0", "None");
valueMap.put("1", "Subtle");
valueMap.put("2", "Intrusive");
notifyTypeItem.setValueMap(valueMap);
Read on for an explanation of what's happening and why it can be a bad approach.
Anonymous Sub-classes
An anonymous class is generally just a class without a name. For example, you can create anonymous instances of an interface like Runnable
:
Runnable r = new Runnable() {
@Override
public void run() {
// Do something
}
};
r.run(); // Does that something
Similarly, you can create anonymous instances of abstract and concrete classes too. For example, it's very common to create an anonymous instance of ThreadLocal
:
private static ThreadLocal<SimpleDateFormat> localIsoDateFormat = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
}
This is useful when you don't need a full, dedicated class to override a method or two, similar to creating anonymous instances of interfaces with only one method.
Instance Initializer Blocks
An instance initializer block allows you to execute initializers outside of your constructor. For example:
public class MyClass {
private final String s;
{
s = "My Class String";
}
public String getS() { return s; }
}
It's essentially a stand-in for a constructor, and it's generally unnecessary, so you rarely see it. It can almost always be moved to a constructor instead.
Combining Them
Your example combines them. It's creating an anonymous subclass of LinkedHashMap
, then it's also using an initializer block. More properly formatted, your code is:
Map<String, String> map = new LinkedHashMap<>() {
{
put("0", "None");
put("1", "Subtle");
put("2", "Intrusive");
}
};
It's an anonymous instance of LinkedHashMap
with an instance initializer block that does the put
calls.
Why is it bad?
For the same reason that you need to be careful creating anonymous classes: references to the enclosing class instance.
Anonymous classes are notorious for being the source of memory leaks in your application. Your code appears to be in a non-static
context. This means that the anonymous LinkedHashMap
subclass you create will have an implicit reference to the class that your method is sitting in. For example, if your method is in MyClass
:
public class MyClass {
private SelectItem notifyTypeItem = new SelectItem();
public void foo() {
notifyTypeItem.setTitle("Default Notification");
notifyTypeItem.setWidth("100%");
notifyTypeItem.setValueMap(new LinkedHashMap<String, String>() {{
put("0", "None");
put("1", "Subtle");
put("2", "Intrusive");
}}
);
}
}
The newly created LinkedHashMap
subclass (MyClass$1
will be the "class name", if you can call it such a thing) will have a reference to the enclosing MyClass
instance. In some cases, this may be fine. But if you're creating the notifyTypeItem
with the intention of passing it to something else and throwing away your MyClass
instance, then you'll be creating a memory leak in your application. The MyClass
instance will be referenced by the MyClass$1
instance, and the SelectItem
will reference the MyClass$1
instance, so the MyClass
instance will never be garbage collected until the SelectItem
instance is no longer referenced. If MyClass
has several other references besides the SelectItem
, then that will only increase the total memory a single MyClass
instance consumes, and lead to more memory leak issues.
References
Here are some relevant links:
- Anonymous Classes Java Trail (Java trail, docs.oracle.com)
- Initializing Fields Java Trail (Java trail, docs.oracle.com)
- What is Double Brace initialization in Java? (SO question, stackoverflow.com)
- Why you shouldn't use the double brace initializer (blog post by Nish Tahir, blog.nishtahir.com)
- When exactly is it leak safe to use (anonymous) inner classes? (SO question, stackoverflow.com)
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.
Meaning of new Class(...){{...}} initialization idiom
It's called double curly brace initialization. (EDIT: Link removed, archived here)
It means you're creating an anonymous subclass and the code within the double braces is basically a constructor. It's often used to add contents to collections because Java's syntax for creating what are essentially collection constants is somewhat awkward.
So you might do:
List<String> list = new ArrayList<String>() {{
add("one");
add("two");
add("three");
}};
instead of:
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
I actually don't like that and prefer to do this:
List<String> list = Arrays.asList("one", "two", "three");
So it doesn't make much sense in that case whereas it does for, say, Maps, which don't have a convenient helper.
Related Topics
Error: Could Not Find or Load Main Class
Running Jmap Getting Unable to Open Socket File
How to Execute Bash Command With Sudo Privileges in Java
Growing Resident Memory Usage (Rss) of Java Process
Com.MySQL.Jdbc.Exceptions.Jdbc4.Communicationsexception: Communications Link Failure
How to Declare and Initialize an Array in Java
How to Convert a String to an Int in Java
How to Stop Repeated Keypressed()/Keyreleased() Events in Swing
Running a Jar File Without Directly Calling 'Java'
Non-Static Variable Cannot Be Referenced from a Static Context
How to Create a Generic Array in Java
What's the Difference Between JavaScript and Java
How to Compare Strings in Java
What Is a Stringindexoutofboundsexception - How to Fix It