Eficiencia de Java "Doble Brace Inicialización"?


En Características ocultas de Java la respuesta principal menciona la inicialización de doble llave , con una sintaxis muy atractiva:

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

Este modismo crea una clase interna anónima con solo un inicializador de instancia en ella, que "puede usar cualquier [...] methods in the containing scope".

Pregunta principal: ¿Es esto como ineficiente como suena? ¿Debería limitarse su uso a inicializaciones únicas? (Y por supuesto presumiendo!)

Segundo pregunta: El nuevo HashSet debe ser el "this" usado en el inicializador de instancias ... ¿puede alguien arrojar luz sobre el mecanismo?

Tercera pregunta: ¿Es este modismo demasiado oscuro para usar en código de producción?

Resumen: Respuestas muy, muy bonitas, gracias a todos. En la pregunta (3), la gente sintió que la sintaxis debería ser clara (aunque recomendaría un comentario ocasional, especialmente si su código pasará a desarrolladores que pueden no estar familiarizados con él).

El pregunta (1), el código generado debería ejecutarse rápidamente. Extra .los archivos de clase causan desorden en los archivos jar y ralentizan ligeramente el inicio del programa (gracias a @coobird por medir eso). @Thilo señaló que la recolección de basura puede verse afectada, y el costo de memoria para las clases extra cargadas puede ser un factor en algunos casos.

La pregunta (2) resultó ser la más interesante para mí. Si entiendo las respuestas, lo que está sucediendo en DBI es que la clase interna anónima extiende la clase de la objeto que está siendo construido por el nuevo operador, y por lo tanto tiene un valor "this" que hace referencia a la instancia que se está construyendo. Muy limpio.

En general, DBI me parece una especie de curiosidad intelectual. Coobird y otros señalan que puede lograr el mismo efecto con matrices.asList, varargs methods, Google Collections, and the proposed Java 7 Collection literals. Los nuevos lenguajes JVM como Scala, JRuby y Groovy también ofrecen notaciones concisas para la construcción de listas e interoperan bien con Java. Dado que DBI estorba la ruta de clases, ralentiza un poco la carga de clases y hace que el código sea un poco más oscuro, probablemente lo evitaría. Sin embargo, planeo darle esto a un amigo que acaba de obtener su SCJP y ama las justas de buen carácter sobre la semántica de Java. ;- ) Gracias a todos!

7/2017: Baeldung tiene un buen resumen de la inicialización de doble llave y lo considera un anti-patrón.

12/2017: @ Basil Bourque señala que en el nuevo Java 9 se puede diga:

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

Ese es sin duda el camino a seguir. Si estás atascado con una versión anterior, echa un vistazo a ImmutableSet de Google Collections.

Author: Jim Ferrans, 2009-05-29

15 answers

Este es el problema cuando me dejo llevar demasiado por las clases internas anónimas:

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

Estas son todas las clases que se generaron cuando estaba haciendo una aplicación simple, y usaron cantidades copiosas de clases internas anónimas each cada clase se compilará en un archivo class separado.

La "inicialización de doble llave", como ya se mencionó, es una clase interna anónima con un bloque de inicialización de instancia, lo que significa que se crea una nueva clase para cada "inicialización", todo con el propósito de hacer generalmente un solo objeto.

Teniendo en cuenta que la Máquina Virtual Java necesitará leer todas esas clases cuando las use, eso puede llevar a algún tiempo en el proceso de verificación de código de bytes y demás. Sin mencionar el aumento en el espacio en disco necesario para almacenar todos esos archivos class.

Parece que hay un poco de sobrecarga cuando se utiliza la inicialización de doble llave, por lo que probablemente no sea una buena idea para ir demasiado por la borda con él. Pero como Eddie ha señalado en los comentarios, no es posible estar absolutamente seguro del impacto.


Solo como referencia, la inicialización de doble llave es la siguiente:

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

Parece una característica "oculta" de Java, pero es solo una reescritura de:

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

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

Así que es básicamente un bloque de inicialización de instancia que es parte de una clase interna anónima.


La Colección Literales de Joshua Bloch la propuesta para del Proyecto Coin estaba en las siguientes líneas:

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

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

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

Tristemente, no hizo su camino ni en Java 7 ni 8 y fue archivado indefinidamente.


Experimento

Aquí está el experimento simple que he probado make haga 1000 ArrayLists con los elementos "Hello" y "World!" agregados a ellos a través del método add, usando los dos métodos:

Método 1: Inicialización de doble llave

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

Método 2: Instanciar un ArrayList y add

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

He creado un programa simple para escribir un archivo fuente Java para realizar 1000 inicializaciones utilizando los dos métodos:

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

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

Tenga en cuenta que el tiempo transcurrido para inicializar los 1000 ArrayList s y las 1000 clases internas anónimas que se extienden ArrayList se comprueba utilizando el System.currentTimeMillis, por lo que el temporizador no tiene una resolución muy alta. En mi sistema Windows, el la resolución es de alrededor de 15-16 milisegundos.

Los resultados de 10 corridas de las dos pruebas fueron los siguientes:

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

Como se puede ver, la inicialización de doble llave tiene un tiempo de ejecución notable de alrededor de 190 ms.

Mientras tanto, el tiempo de ejecución de la inicialización ArrayList resultó ser de 0 ms. Por supuesto, la resolución del temporizador debe tenerse en cuenta, pero es probable que sea inferior a 15 ms.

Por lo tanto, parece haber una diferencia notable en el tiempo de ejecución de los dos métodos. Parece que hay de hecho cierta sobrecarga en los dos métodos de inicialización.

Y sí, hubo 1000 archivos .class generados al compilar el programa de prueba de inicialización de doble llave Test1.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-02 11:20:23

Una propiedad de este enfoque que no se ha señalado hasta ahora es que debido a que se crean clases internas, toda la clase contenedora se captura en su ámbito. Esto significa que mientras su Conjunto esté vivo, retendrá un puntero a la instancia contenedora (this$0) y evitará que se recolecte basura, lo que podría ser un problema.

Esto, y el hecho de que una nueva clase se crea en primer lugar a pesar de que un HashSet regular funcionaría bien (o incluso mejor), hace que yo no quiero usar esta construcción (a pesar de que realmente anhelo el azúcar sintáctico).

Segunda pregunta: El nuevo HashSet debe ser el "this" usado en el inicializador de instancias ... ¿puede alguien arrojar luz sobre el mecanismo? Ingenuamente habría esperado que " esto "se refiriera al objeto inicializando"sabores".

Así es como funcionan las clases internas. Obtienen su propio this, pero también tienen punteros a la instancia padre, por lo que puede llamar a los métodos en el contenido objeta también. En caso de un conflicto de nombres, la clase interna (en su caso HashSet) tiene prioridad, pero puede prefijar "esto" con un nombre de clase para obtener el método externo también.

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

Para ser claro sobre la subclase anónima que se está creando, también podría definir métodos allí. Por ejemplo, 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-27 22:46:57

Cada vez que alguien usa la inicialización de doble corsé, un gatito muere.

Aparte de que la sintaxis es bastante inusual y no realmente idiomática (el sabor es discutible, por supuesto), está creando innecesariamente dos problemas significativos en su aplicación, que acabo de bloguear recientemente con más detalle aquí.

1. Estás creando demasiadas clases anónimas

Cada vez que se usa la inicialización de doble llave, se crea una nueva clase. E. g. este ejemplo:

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

... producirá estas clases:

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

Eso es un poco de sobrecarga para su classloader - para nada! Por supuesto, no tomará mucho tiempo de inicialización si lo haces una vez. Pero si lo hace 20 ' 000 veces a través de su aplicación empresarial... todo ese montón de memoria sólo por un poco de "azúcar de sintaxis"?

2. ¡Estás potencialmente creando una fuga de memoria!

Si toma el código anterior y devuelve ese mapa de un método, los llamantes de ese método podría ser sin sospechar aferrarse a recursos muy pesados que no se pueden recoger basura. Considere el siguiente ejemplo:

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

El Map devuelto ahora contendrá una referencia a la instancia adjunta de ReallyHeavyObject. Probablemente no quieras arriesgarte a que:

Pérdida De Memoria Aquí

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

3. Puede pretender que Java tiene literales de mapa

A responda a su pregunta real, la gente ha estado usando esta sintaxis para pretender que Java tiene algo como literales de mapa, similar a los literales de matriz existentes:

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

Algunas personas pueden encontrar esto sintácticamente estimulante.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-17 08:56:33

Tomando la siguiente clase de prueba:

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

Y luego descompilar el archivo de clase, veo:

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

Esto no me parece terriblemente ineficiente. Si estuviera preocupado por el rendimiento para algo como esto, lo haría perfil. Y tu pregunta #2 es respondida por el código anterior: Estás dentro de un constructor implícito (e inicializador de instancia) para tu clase interna, así que "this" se refiere a esta clase interna.

Sí, esta sintaxis es oscura, pero un comentario puede aclarar uso de sintaxis oscura. Para aclarar la sintaxis, la mayoría de las personas están familiarizadas con un bloque inicializador estático (Inicializadores estáticos JLS 8.7):

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

También puede usar una sintaxis similar (sin la palabra "static") para el uso del constructor (Inicializadores de instancia JLS 8.6), aunque nunca he visto esto usado en código de producción. Esto es mucho menos conocido.

public class Sample2 {
    private final String someVar;

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

Si no tiene un constructor predeterminado, entonces el bloque de código entre { y } se convierte en un constructor por el compilador. Con esto en mente, desentrañar el código de doble llave:

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

El bloque de código entre las llaves internas se convierte en un constructor por el compilador. Las llaves más externas delimitan la clase interna anónima. Para tomar esto el paso final de hacer todo no anónimo:

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

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

Para propósitos de inicialización, yo diría que no hay sobrecarga alguna (o tan pequeña que pueda ser descuidada). Sin embargo, cada uso de flavors no irá en contra de HashSet sino en lugar de MyHashSet. Probablemente hay una pequeña (y muy posiblemente insignificante) sobrecarga a esto. Pero de nuevo, antes de preocuparme por ello, lo perfilaría.

Nuevamente, para tu pregunta #2, el código anterior es el equivalente lógico y explícito de la inicialización de doble llave, y hace obvio dónde "this" se refiere: A la clase interna que se extiende HashSet.

Si tiene preguntas sobre los detalles de los inicializadores de instancia, consulte los detalles en el JLS documentación.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-05-29 13:34:34

Propenso a fugas

He decidido intervenir. El impacto en el rendimiento incluye: operación del disco + descompresión (para jar), verificación de clase, espacio perm-gen (para Sun's Hotspot JVM). Sin embargo, lo peor de todo: es propenso a fugas. No puedes simplemente regresar.

Set<String> getFlavors(){
  return Collections.unmodifiableSet(flavors)
}

Así que si el conjunto escapa a cualquier otra parte cargada por un classloader diferente y se mantiene una referencia allí, se filtrará todo el árbol de clases+classloader. Para evitar esto, es necesaria una copia a HashMap, new LinkedHashSet(new ArrayList(){{add("xxx);add("yyy");}}). No es así ya es lindo. Yo no uso el modismo, en cambio es como 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-22 12:06:24

Cargar muchas clases puede agregar algunos milisegundos al inicio. Si el inicio no es tan crítico y se observa la eficiencia de las clases después del inicio, no hay diferencia.

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

Imprime

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-08 02:10:50

Para crear conjuntos puede usar un método de fábrica varargs en lugar de la inicialización de doble llave:

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

La biblioteca de colecciones de Google tiene un montón de métodos de conveniencia como este, así como un montón de otras funciones útiles.

En cuanto a la oscuridad del modismo, lo encuentro y lo uso en código de producción todo el tiempo. Me preocuparía más que los programadores que se confundan por el idioma se les permita escribir código de producción.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-05-29 04:20:21

Eficiencia aparte, rara vez me encuentro deseando para la creación de colección declarativa fuera de las pruebas unitarias. Creo que la sintaxis de doble llave es muy legible.

Otra forma de lograr la construcción declarativa de listas específicamente es usar Arrays.asList(T ...) así:

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

La limitación de este enfoque es, por supuesto, que no se puede controlar el tipo específico de lista que se generará.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-05-29 05:09:21

Generalmente no hay nada particularmente ineficiente en ello. Generalmente no le importa a la JVM que hayas creado una subclase y agregado un constructor-- eso es algo normal y cotidiano en un lenguaje orientado a objetos. Puedo pensar en casos bastante artificiales en los que podrías causar una ineficiencia al hacer esto (por ejemplo, tienes un método llamado repetidamente que termina tomando una mezcla de diferentes clases debido a esta subclase, mientras que la clase ordinaria pasada sería totalmente predecible) en el último caso, el compilador JIT podría hacer optimizaciones que no son factibles en el primero). Pero en realidad, creo que los casos en los que importará son muy ingeniosos.

Veo el problema más desde el punto de vista de si quieres "desordenar las cosas" con muchas clases anónimas. Como una guía aproximada, considere usar el modismo no más de lo que usaría, por ejemplo, clases anónimas para controladores de eventos.

En (2), estás dentro del constructor de un objeto, así que " esto" se refiere al objeto que estás construyendo. Eso no es diferente a cualquier otro constructor.

En cuanto a (3), eso realmente depende de quién está manteniendo su código, supongo. Si no sabes esto de antemano, entonces un punto de referencia que sugeriría usar es " ¿ves esto en el código fuente del JDK?"(en este caso, no recuerdo haber visto muchos inicializadores anónimos, y ciertamente no en los casos en que ese es el solo contenido de la clase anónima). En la mayoría de los proyectos de tamaño moderado, Yo diría que realmente vas a necesitar a tus programadores para entender la fuente JDK en algún momento u otro, por lo que cualquier sintaxis o lenguaje utilizado allí es "juego limpio". Más allá de eso, yo diría, entrena a la gente en esa sintaxis si tienes control de quién está manteniendo el código, si no comenta o evita.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-05-29 05:26:42

Estaba investigando esto y decidí hacer una prueba más profunda que la proporcionada por la respuesta válida.

Aquí está el código: https://gist.github.com/4368924

Y esta es mi conclusión

Me sorprendió encontrar que en la mayoría de las pruebas de ejecución la iniciación interna era en realidad más rápida (casi el doble en algunos casos). Cuando se trabaja con grandes números, el beneficio parece desvanecerse.

Curiosamente, el caso que crea 3 objetos en el bucle pierde sus beneficios antes que en los otros casos. No estoy seguro de por qué está sucediendo esto y se deben hacer más pruebas para llegar a conclusiones. Crear implementaciones concretas puede ayudar a evitar que la definición de la clase se vuelva a cargar (si eso es lo que está sucediendo)

Sin embargo, está claro que no se observó mucha sobrecarga en la mayoría de los casos para la construcción de un solo elemento, incluso con grandes números.

Un retroceso sería el hecho de que cada una de las llaves dobles iniciaciones crea un nuevo archivo de clase que agrega un bloque de disco completo al tamaño de nuestra aplicación (o aproximadamente 1k cuando se comprime). Una huella pequeña, pero si se usa en muchos lugares podría tener un impacto potencial. Use esto 1000 veces y potencialmente está agregando un MiB completo a su aplicación, lo que puede ser preocupante en un entorno incrustado.

Mi conclusión? Puede estar bien para usar, siempre y cuando no se abusa.

Hazme saber lo que piensas:)

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-24 12:17:41

Secundo la respuesta de Nat, excepto que usaría un bucle en lugar de crear e inmediatamente lanzar la Lista implícita desde 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-10-12 18:26:03

Si bien esta sintaxis puede ser conveniente, también agrega muchas de estas referencias$0 a medida que se anidan y puede ser difícil depurar en los inicializadores a menos que se establezcan puntos de interrupción en cada uno. Por esa razón, solo recomiendo usar esto para los configuradores banales, especialmente los establecidos en constantes, y los lugares donde las subclases anónimas no importan (como la no serialización involucrada).

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-18 20:46:00

Mario Gleichman describe cómo usar funciones genéricas de Java 1.5 para simular literales de Listas de Scala, aunque lamentablemente terminas con Listas inmutables .

Él define esta clase:

package literal;

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

Y lo usa así:

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, ahora parte de Guava apoya una idea similar para la construcción de listas. En esta entrevista, Jared Levy dice:

[...] las características más utilizadas, que aparecen en casi todos los Java class I write, son métodos estáticos que reducen el número de pulsaciones de teclas repetitivas en su código Java. Es muy conveniente poder introducir comandos como los siguientes:

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

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

7/10/2014: Si solo pudiera ser tan simple como 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-11 02:20:29

La inicialización de doble llave es un truco innecesario que puede introducir fugas de memoria y otros problemas

No hay razón legítima para usar este "truco". Guava proporciona buenas colecciones inmutables que incluyen tanto fábricas estáticas como constructores, lo que le permite rellenar su colección donde se declara en una sintaxis limpia, legible y segura .

El ejemplo en la pregunta se convierte en:

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

No solo es más corto y fácil de leer, sino que evita los numerosos problemas con el patrón de doble refuerzo descrito en otras respuestas . Claro, funciona de manera similar a un HashMap directamente construido, pero es peligroso y propenso a errores, y hay mejores opciones.

Cada vez que se encuentre considerando la inicialización con doble refuerzo, debe volver a examinar sus API o introducir otras nuevas para abordar adecuadamente el problema, en lugar de aprovechar los trucos sintácticos.

ahora banderas propensas a errores este anti-patrón .

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-03 21:07:11
  1. Esto llamará add() para cada miembro. Si puedes encontrar una manera más eficiente de poner elementos en un conjunto de hash, entonces usa eso. Ten en cuenta que la clase interna probablemente generará basura, si eres sensible al respecto.

  2. Me parece que el contexto es el objeto devuelto por new, que es el HashSet.

  3. Si necesitas preguntar... Más probable: ¿ la gente que viene después de ti lo sabrá o no? ¿Es fácil de entender y explicar? Si puedes responder "sí" a ambos, siéntase libre de usarlo.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-25 08:12:27