Wydajność Java "Double Brace Initialization"?

In Hidden Features of Java The top response refers Double Brace Initialization , with a very kusząca składnia:

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

Ten idiom tworzy anonimową klasę wewnętrzną z tylko inicjalizatorem instancji, która "może używać dowolnego [...] metod w zakresie zawierającym".

Główne pytanie: czy to jest tak nieefektywne Jak to brzmi? Czy jego użycie powinno być ograniczone do jednorazowych inicjalizacji? (I oczywiście popisywać się!)

Drugi pytanie: Nowy HashSet musi być "this" używane w inicjalizacji instancji ... czy ktoś może rzucić światło na mechanizm?

Trzecie pytanie: czy ten idiom jest zbyt niejasny do użycia w kodzie produkcyjnym?

Podsumowanie: bardzo, bardzo ładne odpowiedzi, dziękuję wszystkim. W pytaniu (3), ludzie uważali, że składnia powinna być jasna (chociaż zalecałbym okazjonalny komentarz, zwłaszcza jeśli twój kod zostanie przekazany programistom, którzy mogą nie być zaznajomieni z nim).

On pytanie (1), wygenerowany kod powinien działać szybko. Extra .pliki klas powodują bałagan w plikach jar i powolne uruchamianie programu (dzięki @coobird za pomiar). @Thilo zwrócił uwagę, że może to mieć wpływ na zbieranie śmieci, a koszt pamięci dla dodatkowych załadowanych klas może być czynnikiem w niektórych przypadkach.

Pytanie (2) okazało się dla mnie najciekawsze. Jeśli Rozumiem odpowiedzi, to w DBI dzieje się tak, że anonimowa Klasa wewnętrzna rozszerza klasę obiekt jest konstruowany przez nowy operator, a zatem ma wartość "this" odnoszącą się do budowanej instancji. Bardzo schludnie.

Ogólnie rzecz biorąc, DBI wydaje mi się czymś w rodzaju ciekawości intelektualnej. Coobird i inni zwracają uwagę, że możesz osiągnąć ten sam efekt z tablicami.asList, varargs methods, Google Collections, and the proposed Java 7 Collection literals. Nowsze języki JVM, takie jak Scala, JRuby i Groovy, również oferują zwięzłe notacje do budowy list i dobrze współdziałają z Javą. Biorąc pod uwagę, że DBI zaśmieca ścieżkę klas, spowalnia Ładowanie klas i sprawia, że kod jest nieco bardziej niejasny, prawdopodobnie uniknęłbym tego. Jednak planuję zrzucić to na przyjaciela, który właśnie dostał swój SCJP i kocha dobre natured jousts o semantyce Javy! ;- ) Dzięki wszystkim!

7/2017: Baeldung ma dobre podsumowanie inicjalizacji podwójnego nawiasu klamrowego i uważa to za anty-wzorzec.

@Basil Bourque zauważa, że w Nowej Javie 9 można powiedz:
Set<String> flavors = Set.of("vanilla", "strawberry", "chocolate", "butter pecan");
/ Align = "left" / Jeśli utkniesz z wcześniejszą wersją, spójrz na zestaw ImmutableSet Google Collections.
Author: Jim Ferrans, 2009-05-29

15 answers

Oto problem, kiedy zbyt mnie poniosą anonimowe klasy wewnętrzne:

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

Są to wszystkie klasy, które zostały wygenerowane, gdy tworzyłem prostą aplikację i używałem dużej ilości anonimowych klas wewnętrznych - każda klasa zostanie skompilowana do osobnego pliku class.

"Double brace initialization", jak już wspomniano, jest anonimową klasą wewnętrzną z blokiem inicjalizacji instancji, co oznacza, że nowa klasa jest tworzona dla każdego "initialization", wszystko w celu zwykle tworzenia jednego obiektu.

Biorąc pod uwagę, że wirtualna maszyna Javy będzie musiała odczytać wszystkie te klasy podczas ich używania, co może prowadzić do pewnego czasu w procesie weryfikacji bajtowego kodu itd. Nie wspominając już o zwiększeniu potrzebnej przestrzeni dyskowej do przechowywania wszystkich plików class.

Wydaje się, że jest trochę narzutu podczas korzystania z inicjalizacji podwójnego nawiasu klamrowego, więc prawdopodobnie nie jest to taki dobry pomysł żeby przesadzić z tym. Ale jak Eddie zauważył w komentarzach, nie można być absolutnie pewnym wpływu.


Dla odniesienia, inicjalizacja podwójnego nawiasu jest następująca:

List<String> list = new ArrayList<String>() {{
    add("Hello");
    add("World!");
}};
W przeciwieństwie do innych języków Java, Java nie jest częścią języka Java.]}
List<String> list = new ArrayList<String>() {

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

Jest to więc w zasadzie blok inicjalizacji instancji , który jest częścią anonimowej klasy wewnętrznej .


Joshua Bloch ' s zbiór literałów propozycja dla projektu Moneta była następująca:

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

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

Map<String, Integer> truthMap = { "answer" : 42 };
[23]} niestety, to[54]}nie dotarło {31]} do Javy 7 ani 8 i zostało odłożone na czas nieokreślony.

Eksperyment

Oto prosty eksperyment, który przetestowałem -- zrób 1000 ArrayLists z elementami "Hello" i "World!" dodanymi do nich za pomocą metody add, używając dwóch metod:

Metoda 1: Inicjalizacja Podwójnego Nawiasu

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

Metoda 2: Instantiate an ArrayList i add

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

Stworzyłem prosty program do zapisu pliku źródłowego Javy do wykonania 1000 inicjalizacji przy użyciu dwóch metod:

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

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

Należy pamiętać, że upłynął czas inicjalizacji 1000 ArrayListS I 1000 anonimowych klas wewnętrznych rozszerzających ArrayList jest sprawdzany za pomocą System.currentTimeMillis, więc timer nie ma bardzo wysokiej rozdzielczości. W moim systemie Windows, rozdzielczość wynosi około 15-16 milisekund.

Wyniki 10 serii dwóch testów były następujące:

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

Jak widać, inicjalizacja podwójnego nawiasu ma zauważalny czas wykonania około 190 ms.

W międzyczasie, ArrayList Czas wykonania inicjalizacji okazał się wynosić 0 ms. oczywiście, rozdzielczość timera powinna być brana pod uwagę, ale prawdopodobnie będzie poniżej 15 ms.

Wydaje się więc, że jest zauważalna różnica w czasie wykonania z dwóch metod. Wydaje się, że w obu metodach inicjalizacji rzeczywiście istnieje pewien narzut.

I tak, było 1000 .class plików wygenerowanych przez kompilację Test1 double brace program testowy inicjalizacji.

 537
Author: coobird,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-02 11:20:23

Jedną z właściwości tego podejścia, która do tej pory nie została wskazana, jest to, że ponieważ tworzysz wewnętrzne klasy, Cała zawierająca je klasa jest przechwytywana w jej zakresie. Oznacza to, że dopóki twój zestaw jest żywy, zachowa wskaźnik do instancji zawierającej (this$0) i uchroni go przed zbieraniem śmieci, co może być problemem.

To, a także fakt, że nowa klasa zostanie utworzona w pierwszej kolejności, mimo że zwykły HashSet będzie działał dobrze (lub nawet lepiej), sprawia, że nie chcę używać tego konstruktu (mimo że bardzo tęsknię za cukrem składniowym).

Drugie pytanie: Nowy HashSet musi być "this" użyte w inicjalizatorze instancji ... czy ktoś może rzucić światło na mechanizm? Naiwnie oczekiwałbym, że "to" odnosi się do obiektu inicjującego "smaki".

Tak właśnie działają wewnętrzne klasy. Otrzymują własne this, ale mają również wskaźniki do instancji nadrzędnej, dzięki czemu można wywoływać metody zawierające obiekt również. W przypadku konfliktu nazw, Klasa wewnętrzna (w Twoim przypadku HashSet) ma pierwszeństwo, ale możesz dodać prefiks "this" z nazwą klasy, aby uzyskać również zewnętrzną metodę.
public class Test {

    public void add(Object o) {
    }

    public Set<String> makeSet() {
        return new HashSet<String>() {
            {
              add("hello"); // HashSet
              Test.this.add("hello"); // outer instance 
            }
        };
    }
}

Aby było jasne, co do tworzonej anonimowej podklasy, możesz zdefiniować tam również metody. Na przykład override HashSet.add()

    public Set<String> makeSet() {
        return new HashSet<String>() {
            {
              add("hello"); // not HashSet anymore ...
            }

            @Override
            boolean add(String s){

            }

        };
    }
 90
Author: Thilo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-27 22:46:57

Za każdym razem, gdy ktoś używa podwójnej klamry, kotek ginie.

Poza tym, że składnia jest dość nietypowa i niezbyt idiomatyczna (smak jest oczywiście dyskusyjny), niepotrzebnie tworzysz dwa istotne problemy w swojej aplikacji, o których niedawno pisałem bardziej szczegółowo tutaj.

1. Tworzysz zbyt wiele anonimowych klas

Za każdym razem, gdy używasz inicjalizacji podwójnego nawiasu klamrowego, tworzona jest nowa klasa. Np. ten przykład:

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

... będzie produkować te klasy:

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

To spore obciążenie dla Twojego klasy-za nic! Oczywiście nie zajmie to dużo czasu inicjalizacji, jeśli zrobisz to raz. Ale jeśli zrobisz to 20'000 razy w całej aplikacji korporacyjnej... cała ta kupa pamięci tylko dla odrobiny "cukru składniowego"?

2. Potencjalnie tworzysz wyciek pamięci!

Jeśli weźmiesz powyższy kod i zwrócisz tę mapę z metody, metoda może być nieświadomie trzymanie się bardzo ciężkich zasobów, które nie mogą być zbierane śmieci. Rozważ następujący przykład:

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

Zwrócona Map będzie teraz zawierać odniesienie do instancji ReallyHeavyObject. Prawdopodobnie nie chcesz ryzykować:

Wyciek Pamięci Tutaj

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

3. Możesz udawać, że Java ma literały map

To odpowiedź na twoje pytanie, ludzie używają tej składni, aby udawać, że Java ma coś w rodzaju literałów map, podobnych do istniejących literałów tablicy:

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

Niektórzy ludzie mogą uznać to za stymulujące składniowo.

 37
Author: Lukas Eder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-17 08:56:33

Biorąc następującą klasę testową:

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

A potem dekompiluję plik klasy, widzę:

public class Test {
  public void test() {
    java.util.Set flavors = new HashSet() {

      final Test this$0;

      {
        this$0 = Test.this;
        super();
        add("vanilla");
        add("strawberry");
        add("chocolate");
        add("butter pecan");
      }
    };
  }
}

To nie wygląda na strasznie nieefektywne dla mnie. Gdybym martwił się o coś takiego, zrobiłbym profil. Na twoje pytanie #2 odpowiada powyższy kod: znajdujesz się wewnątrz niejawnego konstruktora (i inicjalizatora instancji) dla swojej wewnętrznej klasy, więc "this" odnosi się do tej wewnętrznej klasy.

Tak, ta składnia jest niejasna, ale komentarz może wyjaśnić niejasne użycie składni. Aby wyjaśnić składnię, większość ludzi zna statyczny blok inicjalizacji (JLS 8.7 Static Initializers): {]}

public class Sample1 {
    private static final String someVar;
    static {
        String temp = null;
        ..... // block of code setting temp
        someVar = temp;
    }
}

Możesz również użyć podobnej składni (bez słowa " static") do użycia konstruktora (Inicjalizatory instancji JLS 8.6), chociaż nigdy nie widziałem tego w kodzie produkcyjnym. Jest to znacznie mniej znane.

public class Sample2 {
    private final String someVar;

    // This is an instance initializer
    {
        String temp = null;
        ..... // block of code setting temp
        someVar = temp;
    }
}

Jeśli nie masz domyślnego konstruktora, blok kodu pomiędzy { i } jest zamieniany w konstruktor przez kompilator. W tym celu należy rozwikłać kod podwójnej klamry:

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

Blok kodu między nawiasami wewnętrznymi jest zamieniany w konstruktor przez kompilator. Zewnętrzna większość szelek wyznacza anonimową klasę wewnętrzną. Aby zrobić to ostatni krok, aby wszystko było nieanonimowe:

public void test() {
  Set<String> flavors = new MyHashSet();
}

class MyHashSet extends HashSet<String>() {
    public MyHashSet() {
        add("vanilla");
        add("strawberry");
        add("chocolate");
        add("butter pecan");
    }
}

Dla celów inicjalizacji, powiedziałbym, że nie ma żadnych kosztów ogólnych (lub tak małych, że można je pominąć). Jednak każde użycie flavors nie będzie przeciwne HashSet, ale zamiast przeciw MyHashSet. Jest to prawdopodobnie niewielki (i prawdopodobnie znikomy) narzut na to. Ale jeszcze raz, zanim się o to martwiłem, zrobiłem profil.

Ponownie, na twoje pytanie #2, powyższy kod jest logicznym i jawnym odpowiednikiem inicjalizacji podwójnego nawiasu klamrowego i czyni to oczywistym, gdzie "this" odnosi się: do wewnętrznej klasy, która rozszerza HashSet.

Jeśli masz pytania dotyczące szczegółów inicjalizatorów instancji, sprawdź szczegóły w JLS dokumentacja.

 34
Author: Eddie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-29 13:34:34

Podatne na wycieki

Zdecydowałem się wejść. Wpływ wydajności obejmuje: działanie dysku + rozpakowanie (dla jar), weryfikację klasy, przestrzeń Perm-gen (dla Hotspot JVM firmy Sun). Jednak najgorsze jest to, że jest podatny na wycieki. Nie możesz po prostu wrócić.
Set<String> getFlavors(){
  return Collections.unmodifiableSet(flavors)
}

Tak więc, jeśli zestaw przejdzie do innej części ładowanej przez inny classloader i zostanie tam utrzymana Referencja, całe drzewo klas+classloader zostanie wycieknięte. Aby tego uniknąć, potrzebna jest kopia do HashMap, new LinkedHashSet(new ArrayList(){{add("xxx);add("yyy");}}). Nie tak. już uroczo. Sam nie używam idiomu, zamiast tego jest on jak new LinkedHashSet(Arrays.asList("xxx","YYY"));

 33
Author: bestsss,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-22 12:06:24

Ładowanie wielu klas może dodać kilka milisekund do początku. Jeśli uruchomienie nie jest tak krytyczne i patrzysz na efektywność klas po uruchomieniu, nie ma różnicy.

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

Druki

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
 19
Author: Peter Lawrey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-08 02:10:50

Do tworzenia zestawów można użyć metody fabrycznej varargs zamiast inicjalizacji podwójnego nawiasu:

public static Set<T> setOf(T ... elements) {
    return new HashSet<T>(Arrays.asList(elements));
}

Biblioteka zbiorów Google ma wiele wygodnych metod, takich jak ta, a także wiele innych przydatnych funkcji.

Jeśli chodzi o niejasność idiomu, napotykam go i używam go cały czas w kodzie produkcyjnym. Bardziej martwiłbym się programistami, którzy są zdezorientowani idiomem, który pozwala pisać kod produkcyjny.

 16
Author: Nat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-29 04:20:21

Pomijając efektywność, rzadko zdarza mi się pragnąć tworzenia zbioru deklaratywnego poza testami jednostkowymi. Uważam, że składnia podwójnego nawiasu jest bardzo czytelna.

Innym sposobem osiągnięcia deklaratywnej konstrukcji list jest użycie Arrays.asList(T ...) w ten sposób:

List<String> aList = Arrays.asList("vanilla", "strawberry", "chocolate");

Ograniczenie tego podejścia polega oczywiście na tym, że nie można kontrolować określonego typu listy, która ma zostać wygenerowana.

 9
Author: Paul Morie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-29 05:09:21

Generalnie nie ma w tym nic szczególnie nieefektywnego. Ogólnie rzecz biorąc, dla JVM nie ma znaczenia, że stworzyłeś podklasę i dodałeś do niej konstruktor - to normalna, codzienna rzecz do zrobienia w języku obiektowym. Mogę myśleć o dość wymyślnych przypadkach, w których możesz spowodować nieefektywność, robiąc to (np. masz wielokrotnie wywoływaną metodę, która kończy się mieszaniem różnych klas z powodu tej podklasy, podczas gdy zwykła Klasa przekazywana byłaby całkowicie przewidywalne - w tym drugim przypadku kompilator JIT może dokonać optymalizacji, które nie są możliwe w pierwszym). Ale naprawdę, myślę, że sprawy, w których to będzie miało znaczenie, są bardzo wymyślne.

Widzę problem bardziej z punktu widzenia tego, czy chcesz "zaśmiecać sprawy" mnóstwem anonimowych klas. Jako przybliżony przewodnik rozważ użycie idiomu nie bardziej niż użycie, powiedzmy, anonimowych klas do obsługi zdarzeń.

W (2), znajdujesz się wewnątrz konstruktora obiektu, więc " to" odnosi się do obiektu, który budujesz. To nie różni się od innych konstruktorów.

Jeśli chodzi o (3), to naprawdę zależy od tego, kto utrzymuje Twój kod, tak myślę. Jeśli nie wiesz tego z góry, to benchmark, który sugerowałbym użyć to " czy widzisz to w kodzie źródłowym do JDK?"(w tym przypadku nie przypominam sobie, żebym widział wiele anonimowych inicjalizatorów, a już na pewno nie w przypadkach, gdy jest to tylko zawartość klasy anonymous). W większości projektów średniej wielkości, Powiedziałbym, że naprawdę będziesz potrzebował swoich programistów, aby zrozumieć źródło JDK w pewnym momencie lub w innym, więc każda składnia lub idiom użyty tam jest "fair game". Poza tym, powiedziałbym, szkolić ludzi na tej składni, jeśli masz kontrolę nad tym, kto utrzymuje kod, inaczej komentarz lub uniknąć.

 7
Author: Neil Coffey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-29 05:26:42

Badałem to i postanowiłem zrobić bardziej dogłębny test niż ten, który zapewnia prawidłowa odpowiedź.

Oto kod: https://gist.github.com/4368924

I to jest mój wniosek

Byłem zaskoczony, że w większości testów uruchamianie wewnętrzne było w rzeczywistości szybsze(w niektórych przypadkach prawie dwukrotnie). Przy pracy z dużymi liczbami korzyści wydają się zanikać.

Co ciekawe, przypadek, który tworzy 3 obiekty na pętla traci korzyści szybciej niż w innych przypadkach. Nie jestem pewien, dlaczego tak się dzieje i należy przeprowadzić więcej testów, aby wyciągnąć jakiekolwiek wnioski. Tworzenie konkretnych implementacji może pomóc uniknąć przeładowania definicji klasy (jeśli o to chodzi)

Jest jednak jasne, że w większości przypadków nie ma zbyt dużego obciążenia dla pojedynczego budynku, nawet przy dużych liczbach.

Jeden zestaw w tył to fakt, że każdy z podwójnych klamer initiations tworzy nowy plik klasy, który dodaje cały blok dysku do rozmiaru naszej aplikacji (lub około 1k po skompresowaniu). Mały ślad, ale jeśli jest używany w wielu miejscach, może potencjalnie mieć wpływ. Użyj tego 1000 razy, a potencjalnie dodasz do siebie całą aplikację MiB, co może dotyczyć środowiska wbudowanego.

Mój wniosek? Można go używać, o ile nie jest nadużywany.

Daj mi znać, co myślisz:)

 4
Author: pablisco,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-24 12:17:41

Podaję odpowiedź Nat, z tym że użyłbym pętli zamiast tworzyć i od razu wyrzucać listę domniemana z aslist (elements):

static public Set<T> setOf(T ... elements) {
    Set set=new HashSet<T>(elements.size());
    for(T elm: elements) { set.add(elm); }
    return set;
    }
 3
Author: Lawrence Dol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-12 18:26:03

Chociaż ta składnia może być wygodna, dodaje również wiele odwołań$0, gdy te stają się zagnieżdżone i może być trudno wprowadzić debugowanie do inicjalizatorów, chyba że na każdym z nich ustawione są punkty przerwania. Z tego powodu polecam używać tego tylko dla banalnych seterów, szczególnie ustawionych na stałe, i miejsc, w których anonimowe podklasy nie mają znaczenia(jak brak serializacji).

 3
Author: Eric Woodruff,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-18 20:46:00

Mario Gleichman opisuje Jak używać funkcji generycznych Java 1.5 do symulacji literałów listy Scala, choć niestety kończy się to niezmiennymi listami.

Definiuje tę klasę:

package literal;

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

I używa go w ten sposób:

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

Google Collections, obecnie część Guava wspiera podobny pomysł na budowę listy. W tym wywiadzie Jared Levy mówi:]}

[...] najczęściej używanych funkcji, które pojawiają się w prawie każdym Java class I write to statyczne metody zmniejszające liczbę powtarzających się naciśnięć klawiszy w kodzie Javy. Bardzo wygodne jest wprowadzanie poleceń takich jak:

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

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

7/10/2014: gdyby tylko mogło być tak proste jak Python:

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

 3
Author: Jim Ferrans,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-11 02:20:29

Inicjalizacja podwójnego nawiasu to niepotrzebny hack, który może wprowadzić wycieki pamięci i inne problemy

Nie ma uzasadnionego powodu, aby użyć tej "sztuczki". Guava zapewnia ładne niezmienne Kolekcje , które obejmują zarówno statyczne fabryki, jak i budowniczych, pozwalając na wypełnienie kolekcji, gdzie jest zadeklarowana w czystej, czytelnej i bezpiecznej składni.

Przykład w pytaniu staje się:

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

Nie tylko jest krótszy i łatwiejszy do odczytania, ale Inne odpowiedzi. Oczywiście, działa podobnie do bezpośrednio skonstruowanej HashMap, ale jest niebezpieczna i podatna na błędy, a są lepsze opcje.

Za każdym razem, gdy zastanawiasz się nad podwójną inicjalizacją, powinieneś ponownie zbadać swoje API lub wprowadzić nowe, aby poprawnie rozwiązać problem, zamiast korzystać z sztuczek składniowych.

Podatne na Błędy Teraz flagi ten anty-wzór .

 3
Author: dimo414,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-03 21:07:11
  1. Spowoduje to wywołanie add() dla każdego członka. Jeśli możesz znaleźć bardziej efektywny sposób umieszczania elementów w zestawie skrótów, użyj tego. Zauważ, że Klasa wewnętrzna prawdopodobnie wygeneruje śmieci, jeśli jesteś wrażliwy na to.

  2. Wydaje mi się, że kontekstem jest obiekt zwracany przez new, czyli HashSet.

  3. Jeśli chcesz zapytać... Bardziej prawdopodobne: czy ludzie, którzy przyjdą po ciebie, wiedzą o tym, czy nie? Czy jest to łatwe do zrozumienia i wyjaśnienia? Jeśli możesz odpowiedzieć "tak" dla obu, możesz go użyć.

 2
Author: MC Emperor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-25 08:12:27