How efficient is Java's "double support initialization"?

This article was translated from: Efficiency of Java “Double Brace Initialization”?

In Hidden Features of Java the top answer mentions Double Brace Initialization , with a very enticing syntax: Among the hidden features of Java , the best answer mentions Double Brace Initialization , which has a very attractive syntax:

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

This idiom creates an anonymous inner class with just an instance initializer in it, which "can use any [...] methods in the containing scope". This idiom creates an anonymous inner class that contains only an instance initializer, " You can use any [...] method in the include scope. "

Main question: Is this as inefficient as it sounds? Main question: Does this sound inefficient ? Should its use be limited to one-off initializations? Should its use be limited to one- time initialization? (And of course showing off!) (Of course showing off!)

Second question: The new HashSet must be the "this" used in the instance initializer ... can anyone shed light on the mechanism? Second question: The new HashSet must be "this" used in the instance initializer ... Can anyone understand the mechanism?

Third question: Is this idiom too obscure to use in production code? Third question: Is it too vague to use this idiom in production code ?

Summary: Very, very nice answers, thanks everyone. Introduction : Very, very nice answers, thanks everyone . On question (3), people felt the syntax should be clear (though I'd recommend an occasional comment, especially if your code will pass on to developers who may not be familiar with it). In question (3), people think The syntax should be clear (although I recommend commenting occasionally, especially if your code will be passed on to developers who may not be familiar with it).

On question (1), the generated code should run quickly. On question (1), the generated code should run quickly . The extra .class files do cause jar file clutter, and slow program startup slightly (thanks to @coobird for measuring that). The extra .class file will cause the jar file to be confusing and will slow down the program startup speed slightly (thanks to @coobirdMeasurement it). @Thilo pointed out that garbage collection can be affected, and the memory cost for the extra loaded classes may be a factor in some cases. @Thilo pointed out that garbage collection may be affected, and in some cases, the memory cost of additional loaded classes It may be a factor.

Question (2) turned out to be most interesting to me. Question (2) turned out to be most interesting to me . If I understand the answers, what's happening in DBI is that the anonymous inner class extends the class of the object being constructed by the new operator, and hence has a "this" value referencing the instance being constructed. If I understand the answer, then DBI What happens in is that the anonymous inner class extends the class of the object constructed by the new operator, and therefore has a "this" value that references the instance being constructed. Very neat .

Overall, DBI strikes me as something of an intellectual curiousity. Overall, DBI makes me very curious. Coobird and others point out you can achieve the same effect with Arrays.asList, varargs methods, Google Collections, and the proposed Java 7 Collection literals. Coobird and others pointed out that you can use Arrays.asList, varargs methods, Google Collections and proposals The Java 7 Collection text achieves the same effect. Newer JVM languages ​​like Scala, JRuby, and Groovy also offer concise notations for list construction, and interoperate well with Java. Newer JVM languages ​​such as Scala, JRuby, and Groovy also provide concise symbols for list construction and work well with Java Interoperability. Given that DBI clutters up the classpath, slows down class loading a bit, and makes the code a tad more obscure, I'd probably shy away from it. Given that DBI messes up the class path, it slows down the class loading speed and makes the code Even more obscure, I might avoid it. However, I plan to spring this on a friend who's just gotten his SCJP and loves good natured jousts about Java semantics! However, I plan to publish this article on a friend who just got SCJP and like a good friend about Java semantics ! ;-) Thanks everyone! ;-) Thanks everyone!

7/2017: Baeldung has A Good Summary of Double Brace Initialization and considers IT AN Anti-pattern. 7/2017: Baeldung double support initialization of good summary , and that it is an anti-pattern.

12/2017: @Basil Bourque notes that in the new Java 9 you can say: 12/2017: @Basil Bourque pointed out that in the new Java 9 you can say :

Set<String> flavors = Set.of("vanilla", "strawberry", "chocolate", "butter pecan");

That's for sure the way to go. This is definitely the way to go . If you're stuck with an earlier version, take a look at Google Collections 'ImmutableSet . If you encounter an earlier version, check out Google Collections' ImmutableSet .


#1st Floor

Reference: https://stackoom.com/question/3sRp/Java-Double Support Initialization-Efficiency


#2nd Floor

I was researching this and decided to do a more in depth test than the one provided by the valid answer. I was researching this and decided to conduct a more in-depth test than the effective answer provided.

Here is the code: https://gist.github.com/4368924 This is the code: https : //gist.github.com/4368924

and this is my conclusion this is my conclusion

I was surprised to find that in most of the run tests the internal initiation was actually faster (almost double in some cases). I was surprised to find that in most running tests, the internal startup is actually faster (in some cases Almost doubled). When working with large numbers the benefit seems to fade away. When working with large numbers the benefit seems to fade away .

Interestingly, the case that creates 3 objects on the loop loses it's benefit rans out sooner than on the other cases. Interestingly, the case of creating 3 objects in the loop loses its benefits and is faster than other cases. I am not sure why this is happening and more testing should be done to reach any conclusions. I am not sure why this is the case, and more tests should be done to reach any conclusion . Creating concrete implementations may help to avoid the class definition to be reloaded (if that's what's happening) creating concrete implementations may help avoid reloading class definitions (if this is what is happening)

However, it is clear that not much overhead it observed in most cases for the single item building, even with large numbers. However, it is clear that in most cases, even if the number is large, there is not much overhead for building a single project .

One set back would be the fact that each of the double brace initiations creates a new class file that adds a whole disk block to the size of our application (or about 1k when compressed). An example is the creation of a new Class file, it adds the entire disk block to the size of our application (or about 1k when compressed). A small footprint, but if it's used in many places it could potentially have an impact. It has a small footprint, but it may have an impact if used in many places . Use this 1000 times and you are potentially adding a whole MiB to you applicaiton, which may be concerning on an embedded environment. Using this 1000 times, you may add the entire MiB to the application, which may be related to the embedded environment.

My conclusion ? It can be ok to use as long as it is not abused. As long as it is not abused, it can be used.

Let me know what you think :) Let me know what you think :)


#3rd floor

Loading many classes can add some milliseconds to the start. Loading many classes can add some milliseconds to the start . If the startup isn't so critical and you are look at the efficiency of classes after startup there is no difference. If startup is not so critical, you see the efficiency of the classes after startup, there is no difference .

package vanilla.java.perfeg.doublebracket;

import java.util.*;

/**
 * @author plawrey
 */
public class DoubleBracketMain {
    public static void main(String... args) {
        final List<String> list1 = new ArrayList<String>() {
            {
                add("Hello");
                add("World");
                add("!!!");
            }
        };
        List<String> list2 = new ArrayList<String>(list1);
        Set<String> set1 = new LinkedHashSet<String>() {
            {
                addAll(list1);
            }
        };
        Set<String> set2 = new LinkedHashSet<String>();
        set2.addAll(list1);
        Map<Integer, String> map1 = new LinkedHashMap<Integer, String>() {
            {
                put(1, "one");
                put(2, "two");
                put(3, "three");
            }
        };
        Map<Integer, String> map2 = new LinkedHashMap<Integer, String>();
        map2.putAll(map1);

        for (int i = 0; i < 10; i++) {
            long dbTimes = timeComparison(list1, list1)
                    + timeComparison(set1, set1)
                    + timeComparison(map1.keySet(), map1.keySet())
                    + timeComparison(map1.values(), map1.values());
            long times = timeComparison(list2, list2)
                    + timeComparison(set2, set2)
                    + timeComparison(map2.keySet(), map2.keySet())
                    + timeComparison(map2.values(), map2.values());
            if (i > 0)
                System.out.printf("double braced collections took %,d ns and plain collections took %,d ns%n", dbTimes, times);
        }
    }

    public static long timeComparison(Collection a, Collection b) {
        long start = System.nanoTime();
        int runs = 10000000;
        for (int i = 0; i < runs; i++)
            compareCollections(a, b);
        long rate = (System.nanoTime() - start) / runs;
        return rate;
    }

    public static void compareCollections(Collection a, Collection b) {
        if (!a.equals(b) && a.hashCode() != b.hashCode() && !a.toString().equals(b.toString()))
            throw new AssertionError();
    }
}

prints prints

double braced collections took 36 ns and plain collections took 36 ns
double braced collections took 34 ns and plain collections took 36 ns
double braced collections took 36 ns and plain collections took 36 ns
double braced collections took 36 ns and plain collections took 36 ns
double braced collections took 36 ns and plain collections took 36 ns
double braced collections took 36 ns and plain collections took 36 ns
double braced collections took 36 ns and plain collections took 36 ns
double braced collections took 36 ns and plain collections took 36 ns
double braced collections took 36 ns and plain collections took 36 ns

#4th floor

Gleichman Mario Describes How to use the Generic Java 1.5 Functions to Simulate Scala List literals, though sadly you Wind up with immutable Lists. Mario Gleichman describe how to use the Java 1.5 generic function to simulate Scala List text, although unfortunately you end up Immutable list.

He defines this class: He defines this class:

package literal;

public class collection {
    public static <T> List<T> List(T...elems){
        return Arrays.asList( elems );
    }
}

and uses it thusly :

import static literal.collection.List;
import static system.io.*;

public class CollectionDemo {
    public void demoList(){
        List<String> slist = List( "a", "b", "c" );
        List<Integer> iList = List( 1, 2, 3 );
        for( String elem : List( "a", "java", "list" ) )
            System.out.println( elem );
    }
}

The Collections Google, now Part of Guava the Supports for List A Similar IDEA Construction. Google is now the Collections Guava's part, it supports the idea of a list of similar construction. In this interview , Jared Levy says: In this interview , Jared Levy said:

[...] the most heavily-used features, which appear in almost every Java class I write, are static methods that reduce the number of repetitive keystrokes in your Java code. [...] The most commonly used features, almost appear in Every Java class I write is a static method, which can reduce the number of repeated keystrokes in Java code. It's so convenient being able to enter commands like the following: It is very convenient to be able to enter the following commands:

Map<OneClassWithALongName, AnotherClassWithALongName> = Maps.newHashMap();

List<String> animals = Lists.immutableList("cat", "dog", "horse");

7/10/2014: If only it could be as simple as Python's: July 10, 2014: If it is as simple as Python:

animals = ['cat', 'dog', 'horse']


#5th Floor

While this syntax can be convenient, it also adds a lot of this $ 0 references as these become nested and it can be difficult to step debug into the initializers unless breakpoints are set on each one. Although this syntax is convenient, it will also Add a lot of this $ 0 references because they will be nested, and unless you set a breakpoint on each reference, it will be difficult to debug steps into the initializer. For that reason, I only recommend using this for banal setters, especially set to constants, and places where anonymous subclasses don't matter (like no serialization involved). For this reason, I only recommend it for mediocre setters, In particular, setting it to a constant, and where the anonymous subclass is irrelevant (such as not involving serialization).


#6th floor

Every time someone uses double brace initialisation, a kitten gets killed. Every time someone uses double brace initialisation, a kitten gets killed .

Apart from the syntax being rather unusual and not really idiomatic (taste is debatable, of course), you are unnecessarily creating two significant problems in your application, which I've just recently blogged about in more detail here . Except that the syntax is quite unusual and Not really idiomatic (of course the taste is controversial), you have unnecessarily created two important questions in the application, which I have discussed in more detail here recently .

1. You're creating way too many anonymous classes you create too many anonymous course

Each time you use double brace initialisation a new class is made. Every time you use double brace initialization, a new class is generated. Eg this example: For example this example:

Map source = new HashMap(){{
    put("firstName", "John");
    put("lastName", "Smith");
    put("organizations", new HashMap(){{
        put("0", new HashMap(){{
            put("id", "1234");
        }});
        put("abc", new HashMap(){{
            put("id", "5678");
        }});
    }});
}};

... will produce these classes: ... will produce these classes:

Test$1$1$1.class
Test$1$1$2.class
Test$1$1.class
Test$1.class
Test.class

That's quite a bit of overhead for your classloader-for nothing! This is a considerable overhead for your classloader-nothing ! Of course it won't take much initialisation time if you do it once. Of course, if you do it once , it will not require much initialization time. But if you do this 20'000 times throughout your enterprise application ... all that heap memory just for a bit of "syntax sugar"? However, if you perform this operation 20'000 times throughout the enterprise application ... All heap memory is just for a little "syntactic sugar"?

2. You're potentially creating a memory leak! You may cause a memory leak!

If you take the above code and return that map from a method, callers of that method might be unsuspectingly holding on to very heavy resources that cannot be garbage collected. If you use the above code and return the map from the method, then the method ’s The caller may undoubtedly retain very heavy resources, which cannot be garbage collected. Consider the following example: Consider the following example :

public class ReallyHeavyObject {

    // Just to illustrate...
    private int[] tonsOfValues;
    private Resource[] tonsOfResources;

    // This method almost does nothing
    public Map quickHarmlessMethod() {
        Map source = new HashMap(){{
            put("firstName", "John");
            put("lastName", "Smith");
            put("organizations", new HashMap(){{
                put("0", new HashMap(){{
                    put("id", "1234");
                }});
                put("abc", new HashMap(){{
                    put("id", "5678");
                }});
            }});
        }};

        return source;
    }
}

Returned of The MapWill now to Contain A Reference The instance of the enclosing ReallyHeavyObject. Returns Mapwill now contain ReallyHeavyObjectthe closure of example ReallyHeavyObject. You probably don't want to risk that: You probably don't want to take this risk:

Memory leak is here

Image from http://blog.jooq.org/2014/12/08/dont-be-clever-the-double-curly-braces-anti-pattern/Image from http://blog.jooq.org/2014/ 12/08 / dont-be-clever-the-double-curly-braces-anti-pattern /

3. You can pretend that Java has map literals you can pretend to have a map text Java

To answer your actual question, people have been using this syntax to pretend that Java has something like map literals, similar to the existing array literals: To answer your actual question, people have been using this syntax to pretend that Java has something similar to existing Map text for array text:

String[] array = { "John", "Doe" };
Map map = new HashMap() {{ put("John", "Doe"); }};

Some people may find this syntactically stimulating. Some people may find this syntactically stimulating .

Published 0 original articles · praised 75 · 560,000 views +

Guess you like

Origin blog.csdn.net/w36680130/article/details/105429982