Efficacité de L'initialisation Java "Double Brace"?

Dans "151930920 des Fonctionnalités Cachées de Java la réponse sommet mentionne Double Croisillon d'Initialisation , avec un très alléchantes syntaxe:

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

cet idiome crée une classe interne anonyme avec juste un initialiseur d'instance, qui" peut utiliser n'importe quelle [...] méthodes dans le domaine d'application contenant".

question principale: est-ce que c'est comme inefficace comme il sonne? Son utilisation soit limitée à des initialisations? (Et bien montrer!)

deuxième question: le nouveau HashSet doit être le" Ceci " utilisé dans l'initialiseur d'instance ... quelqu'un peut-il faire la lumière sur le mécanisme?

troisième question: cet idiome est-il trop obscur à utiliser dans le code de production?

résumé: très, très belles réponses, merci à tous. Sur la question (3), les gens je pense que la syntaxe devrait être claire (bien que je recommande un commentaire occasionnel, surtout si votre code sera transmis à des développeurs qui ne sont peut-être pas familiers avec elle).

à la question (1), le code généré doit être exécuté rapidement. Supplémentaire. les fichiers de classe provoquent un désordre de fichier jar, et ralentissent légèrement le démarrage du programme (merci à @coobird pour avoir mesuré cela). @Thilo a souligné que la collecte des ordures peut être affectée, et le coût de mémoire pour les classes chargées supplémentaires peut être un facteur dans certains cas.

la Question (2) s'est avérée très intéressante pour moi. Si je comprends les réponses, ce qui se passe dans DBI est que la classe interne anonyme étend la classe de l'objet construit par le nouvel opérateur, et a donc une valeur "ceci" se référant à l'instance construite. Très soigné.

dans l'ensemble, DBI me semble quelque peu curieuse sur le plan intellectuel. Coobird et d'autres soulignent que vous pouvez obtenir le même effet avec les Tableaux.asList, les méthodes varargs, les collections Google et les documents proposés de la collection Java 7. Les nouveaux langages JVM comme Scala, JRuby et Groovy offrent également des notations concises pour la construction de listes, et interfèrent bien avec Java. Étant donné que DBI embrouille le chemin de la classe, ralentit un peu le chargement de la classe, et rend le code un peu plus obscur, je serais probablement timide loin de lui. Cependant, je prévois de le faire sur un ami qui vient d'obtenir son SCJP et aime les joutes de bonne nature sur Java la sémantique! ;-) Merci à tous!

7/2017: Baeldung a un bon résumé de l'initialisation double brace et considère qu'il est un anti-modèle.

12/2017: @Basil Bourque note que dans le nouveau Java 9 on peut dire:

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

C'est bien la route à suivre. Si vous êtes bloqué avec une version antérieure, jetez un oeil à Google Collections' ImmutableSet .

696
demandé sur Jim Ferrans 2009-05-29 07:40:13
la source

15 ответов

voici le problème quand je me laisse trop emporter par des classes intérieures anonymes:

2009/05/27  16:35             1,602 DemoApp2.class
2009/05/27  16:35             1,976 DemoApp2.class
2009/05/27  16:35             1,919 DemoApp2.class
2009/05/27  16:35             2,404 DemoApp2.class
2009/05/27  16:35             1,197 DemoApp2.class

/* snip */

2009/05/27  16:35             1,953 DemoApp2.class
2009/05/27  16:35             1,910 DemoApp2.class
2009/05/27  16:35             2,007 DemoApp2.class
2009/05/27  16:35               926 DemoApp2.class
2009/05/27  16:35             4,104 DemoApp2.class
2009/05/27  16:35             2,849 DemoApp2.class
2009/05/27  16:35               926 DemoApp2.class
2009/05/27  16:35             4,234 DemoApp2.class
2009/05/27  16:35             2,849 DemoApp2.class

/* snip */

2009/05/27  16:35               614 DemoApp2.class
2009/05/27  16:35             2,344 DemoApp2.class
2009/05/27  16:35             1,551 DemoApp2.class
2009/05/27  16:35             1,604 DemoApp2.class
2009/05/27  16:35             1,809 DemoApp2.class
2009/05/27  16:35             2,022 DemoApp2.class

ce sont toutes les classes qui ont été générées lorsque je faisais une application simple, et utilisé des quantités abondantes de classes internes anonymes -- chaque classe sera compilée dans un fichier séparé class .

l '"initialisation à double brace", comme déjà mentionné, est une classe interne anonyme avec un bloc d'initialisation d'instance, qui signifie qu'une nouvelle classe est créée pour chaque "initialisation", tous dans le but de faire un objet unique.

considérant que la machine virtuelle Java devra lire toutes ces classes lors de leur utilisation, ce qui peut conduire à un certain temps dans le bytecode verification processus et ainsi de suite. Pour ne pas mentionner l'augmentation de l'espace disque nécessaire pour stocker tous ces "151990920 de fichiers".

il semble qu'il y ait une un peu de overhead lors de l'utilisation double-brace initialisation, il est donc probablement pas une bonne idée d'aller trop à la mer avec elle. Mais comme Eddie a noté dans les commentaires, il n'est pas possible d'être absolument sûr de l'impact.


juste pour référence, l'initialisation à double attelle est la suivante:

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

Il ressemble à un "caché" de Java, mais c'est juste une réécriture de:

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

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

donc c'est essentiellement un " instance initialization block qui fait partie d'une classe interne anonyme .


Joshua Bloch "1519500920 de la Collection" les Littéraux de proposition pour Projet de Pièce de monnaie a été le long des lignes de:

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

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

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

malheureusement, il n'a pas fait son chemin dans Ni Java 7 Ni 8 et a été abandonné indéfiniment.


expérience

Voici l'expérience simple que j'ai testé -- faire 1000 ArrayList s avec les éléments "Hello" et "World!" ajoutés à eux via la méthode add , en utilisant les deux méthodes:

Méthode 1: Double Croisillon D'Initialisation

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

méthode 2: Instantiate an ArrayList et add

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

j'ai créé un programme simple pour écrire un fichier source Java pour effectuer 1000 initialisations en utilisant les deux méthodes:

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

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

veuillez noter que le temps écoulé pour initialiser le 1000 ArrayList s et le 1000 classes intérieures anonymes prolongeant ArrayList est vérifié en utilisant le System.currentTimeMillis , de sorte que la minuterie n'a pas une très haute résolution. Sur mon système Windows, la résolution est d'environ 15-16 millisecondes.

les résultats de 10 essais ont été les suivants:

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

comme on peut le voir, l'initialisation à double attelle a un temps d'exécution perceptible d'environ 190 ms.

pendant ce temps, le ArrayList le temps d'exécution de l'initialisation est de 0 ms. Bien sûr, la résolution sur le minuteur doit être prise en compte, mais il est probable qu'elle soit inférieure à 15 ms.

Donc, il semble y avoir une différence notable dans le temps d'exécution des deux méthodes. Il semble bien que les deux méthodes d'initialisation comportent un certain nombre de frais généraux.

et oui, il y avait 1000 .class fichiers générés en compilant le Test1 double initialisation brace programme de test.

537
répondu coobird 2016-12-02 14:20:23
la source

une propriété de cette approche qui n'a pas été soulignée jusqu'à présent est que parce que vous créez des classes intérieures, l'ensemble contenant la classe est capturé dans sa portée. Cela signifie que tant que votre ensemble est vivant, il conservera un pointeur vers l'instance contenant ( this"151920920" ) et empêchera que cela soit collectionné, ce qui pourrait être un problème.

Cela, et le fait qu'une nouvelle classe est créée, en premier lieu, même si régulièrement HashSet serait de travailler juste très bien (ou même mieux), me fait ne pas vouloir utiliser cette construction (même si Je désire vraiment le sucre syntaxique).

deuxième question: le nouveau HashSet doit être le" Ceci " utilisé dans l'initialiseur d'instance ... quelqu'un peut-il faire la lumière sur le mécanisme? J'aurais naïvement attendu "ceci "pour faire référence à l'objet initialisant"saveurs".

C'est comme ça que fonctionnent les classes intérieures. Ils obtiennent leur propre this , mais ils ont aussi des pointeurs vers l'instance parent, de sorte que vous pouvez également appeler des méthodes sur l'objet containing. En cas de conflit de nom, la classe interne (dans votre cas HashSet) a priorité, mais vous pouvez préfixer "ceci" avec un nom de classe pour obtenir la méthode externe aussi.

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

pour être clair sur la sous-classe anonyme étant créé, vous pourriez définir des méthodes là-dedans aussi bien. Par exemple, outrepasser HashSet.add()

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

            @Override
            boolean add(String s){

            }

        };
    }
90
répondu Thilo 2010-07-28 02:46:57
la source

chaque fois que quelqu'un utilise l'initialisation à double attelle, un chaton se fait tuer.

en dehors de la syntaxe étant plutôt inhabituelle et pas vraiment idiomatique (le goût est discutable, bien sûr), vous créez inutilement deux problèmes significatifs dans votre application, que je viens de bloguer sur plus de détails ici .

1. Vous créez beaucoup trop de classes anonymes

chaque fois que vous utilisez l'initialisation à double attelle, une nouvelle classe est créée. Par exemple: cet exemple:

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

... produira ces classes:

Test.class
Test.class
Test.class
Test.class
Test.class

C'est un peu trop pour votre classloader - pour rien! Bien sûr, il ne prendra pas beaucoup de temps d'initialisation si vous le faites une fois. Mais si vous faites cela 20'000 fois tout au long de votre application d'entreprise... tout ce tas de mémoire juste pour un peu de "syntaxe sugar"?

2. Tu es en train de créer une fuite de mémoire!

si vous prenez le code ci-dessus et que vous retournez cette carte à partir d'une méthode, les appelants de cette méthode pourraient se raccrocher à des ressources très lourdes qui ne peuvent pas être ramassées. Prenons l'exemple suivant:

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

le renvoi Map contiendra maintenant une référence à l'instance d'inclusion de ReallyHeavyObject . Vous ne voulez probablement pas risquer que:

Memory Leak Right Here

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

3. Vous pouvez prétendre que Java a des cartes littérales

pour répondre à votre question actuelle, les gens ont utilisé cette syntaxe pour prétendre que Java a quelque chose comme map literals, similaire à la matrice existante literals:

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

certaines personnes peuvent trouver cela stimulant sur le plan syntaxique.

37
répondu Lukas Eder 2014-12-17 11:56:33
la source

dans la classe d'épreuve suivante:

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

et en décomposant le fichier de classe, je vois:

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

      final Test this"151910920";

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

cela ne me semble pas terriblement inefficace. Si je m'inquiétais de la performance pour quelque chose comme ça, je le profilerais. Et votre question #2 est répondue par le code ci-dessus: vous êtes à l'intérieur d'un constructeur implicite (et initialiseur d'instance) pour votre classe intérieure, donc " this " se réfère à cette classe intérieure.

oui, cette syntaxe est obscure, mais un commentaire peut clarifier l'usage obscur de la syntaxe. Pour clarifier la syntaxe, la plupart des gens sont familiers avec un bloc static initializer (JLS 8.7 Static Initializers):

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

vous pouvez également utiliser une syntaxe similaire (sans le mot static ") pour l'usage du constructeur (JLS 8.6 Instance initialiseurs), bien que je n'ai jamais vu cela utilisé dans le code de production. C'est beaucoup moins connu.

public class Sample2 {
    private final String someVar;

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

Si vous n'avez pas de constructeur par défaut, le bloc de code entre { et } est transformé en un constructeur par le compilateur. En gardant cela à l'esprit, démêlez le code à double attelle:

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

le bloc de code entre les accolades internes est transformé en un constructeur par le compilateur. Les accolades externes délimitent la classe interne anonyme. Pour prendre ceci la dernière étape de rendre tout non-anonyme:

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

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

à des fins d'initialisation, je dirais qu'il n'y a aucun frais généraux (ou si petit qu'il peut être négligé). Cependant, toute utilisation de flavors n'ira pas contre HashSet mais contre MyHashSet . Il y a probablement une petite (et très probablement négligeable de charge). Mais encore une fois, avant de m'en inquiéter, je l'aurais profilé.

encore une fois, à votre question #2, le code ci-dessus est le logique et explicite l'équivalent de l'initialisation à double attelle, et il rend évident où " this " se réfère: à la classe intérieure qui s'étend HashSet .

si vous avez des questions sur les détails des initialisateurs d'instance, consultez les détails dans la documentation JLS .

34
répondu Eddie 2009-05-29 17:34:34
la source

sujet aux fuites

j'ai décidé d'intervenir. L'impact sur les performances inclut: l'opération disque + unzip (pour jar), la vérification de classe, l'espace perm-gen (pour Sun's Hotspot JVM). Cependant, le pire de tous: c'est une fuite sur le ventre. Tu ne peux pas simplement revenir.

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

ainsi, si l'ensemble s'échappe vers une autre partie chargée par un classloader différent et qu'une référence y est conservée, l'arbre entier des classes+classloader sera fuir. Pour éviter cela, une copie à HashMap est nécessaire, new LinkedHashSet(new ArrayList(){{add("xxx);add("yyy");}}) . Pas très joli, pas plus. Je n'utilise pas l'idiome, moi-même, à la place il est comme new LinkedHashSet(Arrays.asList("xxx","YYY"));

33
répondu bestsss 2013-08-22 16:06:24
la source
Le chargement de

plusieurs classes peuvent ajouter quelques millisecondes au démarrage. Si le démarrage n'est pas critique et que vous regardez l'efficacité des classes après le démarrage il n'y a pas de différence.

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
répondu Peter Lawrey 2014-04-08 06:10:50
la source

pour créer des ensembles, vous pouvez utiliser une méthode d'usine varargs au lieu de l'initialisation à double brace:

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

la bibliothèque de collections Google a beaucoup de méthodes pratiques comme celle-ci, ainsi que des charges d'autres fonctionnalités utiles.

quant à l'obscurité de l'idiome, je le rencontre et je l'utilise dans le code de production tout le temps. Je serais plus préoccupé par les programmeurs qui deviennent confus par l'idiome étant autorisé à écrire la production code.

16
répondu Nat 2009-05-29 08:20:21
la source

L'efficacité mise à part, je me retrouve rarement à souhaiter la création d'une collection déclarative en dehors des tests unitaires. Je crois que la syntaxe de la double attelle est très lisible.

une autre façon d'obtenir l'interprétation déclarative des listes est d'utiliser Arrays.asList(T ...) comme suit:

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

la limitation de cette approche est bien sûr que vous ne pouvez pas contrôler le type spécifique de liste à générer.

9
répondu Paul Morie 2009-05-29 09:09:21
la source

il n'y a généralement rien de particulièrement inefficace à ce sujet. Il n'est généralement pas important pour la JVM que vous ayez créé une sous-classe et ajouté un constructeur à celle-ci-- c'est une chose normale, courante à faire dans un langage orienté objet. Je peux penser à des cas assez compliqués où vous pourriez causer une inefficacité en faisant cela (par exemple, vous avez une méthode appelée à plusieurs reprises qui finit par prendre un mélange de différentes classes en raison de cette sous-classe, alors que la classe ordinaire passé dans serait totalement prévisible-- dans ce dernier cas, le compilateur JIT pourrait faire des optimisations qui ne sont pas réalisables dans le premier). Mais vraiment, je pense que les cas où cela aura de l'importance sont très compliqués.

je verrais la question plus du point de vue de savoir si vous voulez "embrouiller les choses" avec beaucoup de classes anonymes. Comme guide approximatif, envisagez d'utiliser l'idiome pas plus que vous utiliseriez, disons, des classes anonymes pour les gestionnaires d'événements.

En (2), vous êtes à l'intérieur le constructeur d'un objet, donc "ceci" se réfère à l'objet que vous construisez. Ce n'est pas différent de n'importe quel autre constructeur.

quant à (3), Cela dépend vraiment de qui maintient votre code, je suppose. Si vous ne le savez pas à l'avance, alors un point de repère que je suggère d'utiliser est "Voyez-vous cela dans le code source du JDK?"(dans ce cas, je ne me souviens pas avoir vu beaucoup d'initialisateurs anonymes, et certainement pas dans les cas où c'est le seulement contenu de la classe anonyme). Dans la plupart des projets de taille moyenne, je dirais que vous allez vraiment avoir besoin de vos programmeurs pour comprendre la source JDK à un moment ou un autre, donc toute syntaxe ou idiome utilisé il ya "fair game". Au-delà de cela, je dirais, former les gens sur cette syntaxe si vous avez le contrôle de qui maintient le code, sinon Commenter ou éviter.

7
répondu Neil Coffey 2009-05-29 09:26:42
la source

je faisais des recherches à ce sujet et j'ai décidé de faire un test plus approfondi que celui fourni par la réponse valide.

voici le code: https://gist.github.com/4368924

et voici ma conclusion

j'ai été surpris de constater que dans la plupart des essais de fonctionnement, l'amorçage interne était en fait plus rapide (presque le double dans certains cas). En travaillant avec un grand nombre de l'avantage semble estomper.

fait intéressant, le cas qui crée 3 objets sur la boucle perd son avantage se retrouve plus tôt que sur les autres cas. Je ne sais pas pourquoi cela se produit et il faudrait procéder à d'autres tests pour tirer des conclusions. Créer des implémentations concrètes peut aider à éviter la définition de classe à recharger (si c'est ce qui se passe)

cependant, il est clair que peu de frais généraux, il a observé dans la plupart des cas pour le seul article construction, même avec de grands nombres.

un retour en arrière serait le fait que chacune des initiations de double barre crée un nouveau fichier de classe qui ajoute un bloc de disque entier à la taille de notre application (ou environ 1k lorsqu'il est comprimé). Une petite empreinte, mais si elle est utilisée dans de nombreux endroits, elle pourrait avoir un impact. Utilisez ce 1000 fois et vous êtes potentiellement en train d'ajouter une MiB entière à votre applicaiton, ce qui peut être inquiétant sur un environnement embarqué.

ma conclusion? Il peut être ok pour l'utiliser tant qu'il n'est pas abusé.

Laissez-moi savoir ce que vous en pensez :)

4
répondu pablisco 2012-12-24 16:17:41
la source

j'appuie la réponse de Nat, sauf que j'utiliserais une boucle au lieu de créer et de lancer immédiatement la liste implicite d'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
répondu Lawrence Dol 2009-10-12 22:26:03
la source

bien que cette syntaxe puisse être pratique, elle ajoute aussi beaucoup de ces références à 0$car elles deviennent imbriquées et il peut être difficile de faire un pas de débogage dans les initialiseurs à moins que des points de rupture soient définis sur chacune d'elles. Pour cette raison, Je ne recommande l'utilisation de ce paramètre que pour les setters banaux, en particulier les setters à constantes, et les endroits où les sous-classes anonymes n'ont pas d'importance (comme aucune sérialisation impliquée).

3
répondu Eric Woodruff 2014-06-19 00:46:00
la source

Mario Gleichman décrit comment utiliser les fonctions génériques Java 1.5 pour simuler la liste Scala littérales, bien que malheureusement vous vous retrouvez avec immuable listes.

il définit cette classe:

package literal;

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

et l'utilise donc:

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, maintenant partie de Guava soutient une idée similaire pour la construction de listes. In cette interview , Jared Levy dit:

[... les fonctionnalités les plus utilisées, qui apparaissent dans presque toutes les classes Java que J'écris, sont des méthodes statiques qui réduisent le nombre de frappes répétitives dans votre code Java. C'est tellement pratique de pouvoir entrer des commandes comme suit:

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

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

7/10/2014: si seulement il pouvait être aussi simple que Python:

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

3
répondu Jim Ferrans 2014-07-11 06:20:29
la source

l'initialisation à double corps est un piratage inutile qui peut introduire des fuites de mémoire et d'autres problèmes

il n'y a aucune raison légitime d'utiliser ce "truc". Gava fournit nice collections immuables qui comprennent à la fois les usines statiques et les constructeurs, vous permettant de peupler votre collection où il est déclaré dans un propre, lisible, et Sécuritaire syntaxe.

L'exemple dans la question devient:

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

non seulement est-il plus court et plus facile à lire, mais il évite les nombreux problèmes avec le modèle à deux brides décrit dans autres réponses . Bien sûr , il fonctionne de la même manière qu'un HashMap construit directement, mais il est dangereux et sujet à erreur, et il ya de meilleures options.

chaque fois que vous envisagez une initialisation à double boucle, vous devriez réexaminer votre API ou introduire nouveaux pour bien aborder la question, plutôt que de tirer profit des astuces syntaxiques.

Erreurs maintenant drapeaux cet anti-modèle .

3
répondu dimo414 2018-04-04 00:07:11
la source
  1. cela s'appellera add() pour chaque membre. Si vous pouvez trouver un moyen plus efficace de mettre les éléments dans une table de hachage ensemble, puis l'utiliser. Notez que la classe intérieure va probablement générer des déchets, si vous êtes sensible à ce sujet.

  2. il me semble que le contexte est l'objet retourné par new , qui est le HashSet .

  3. Si vous avez besoin de demander... Plus susceptibles: les personnes qui viennent après vous savez cela ou non? Est-il facile à comprendre et à expliquer? Si vous pouvez répondre "oui" à la fois, n'hésitez pas à l'utiliser.

2
répondu MC Emperor 2018-07-25 11:12:27
la source