web-dev-qa-db-fra.com

Efficacité de Java "Initialisation Double Brace"?

Dans Fonctionnalités cachées de Java , la première réponse mentionne Initialisation à double hauban , avec une syntaxe séduisante très:

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

Cet idiome crée une classe interne anonyme contenant uniquement un initialiseur d'instance, qui "peut utiliser n'importe quelle [...] méthode dans la portée contenant".

Question principale: Est-ce aussi inefficace que ? Son utilisation devrait-elle être limitée à des initialisations uniques? (Et bien sûr montrer!)

Deuxième question: le nouveau HashSet doit être le "ceci" utilisé dans l'initialiseur d'instance ... Quelqu'un peut-il nous éclairer 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 bonnes réponses, merci à tous. Sur la question (3), les utilisateurs ont estimé que la syntaxe devrait être claire (je recommanderais toutefois un commentaire occasionnel, surtout si votre code sera transmis aux développeurs qui ne le connaissent peut-être pas).

Sur la question (1), le code généré doit être exécuté rapidement. Les fichiers .class supplémentaires génèrent un fouillis de fichiers jar et ralentissent légèrement le démarrage du programme (merci à @coobird d’avoir mesuré cela). @Thilo a souligné que la récupération de place peut être affectée et que le coût en mémoire pour les classes surchargées 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, c'est que la classe interne anonyme étend la classe de l'objet en cours de construction par l'opérateur new, et a donc une valeur "this" faisant référence à l'instance en cours de construction. Très propre.

Dans l’ensemble, DBI me semble une sorte de curiosité intellectuelle. Coobird et d’autres soulignent que vous pouvez obtenir le même effet avec Arrays.asList, les méthodes varargs, Google Collections et les littéraux proposés de Java 7 Collection. Les langages JVM plus récents tels que Scala, JRuby et Groovy offrent également des notations concises pour la construction de listes et interagissent bien avec Java. Étant donné que DBI encombre le chemin de classe, ralentit un peu le chargement de la classe et rend le code un peu plus obscur, je le craindrais probablement. Cependant, j’ai l’intention de lancer ceci sur un ami qui vient de recevoir son SCJP et qui adore les joutes enjouées sur la sémantique Java! ;-) Merci tout le monde!

7/2017: Baeldung a un bon résumé d'initialisation à double accolade et le considère comme un anti-motif.

12/2017: @Basil Bourque note que dans le nouveau Java 9, vous pouvez dire:

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

C'est à coup sûr la voie à suivre. Si vous êtes coincé avec une version antérieure, consultez ImmutableSet de Google Collections .

770
Jim Ferrans

Voici le problème lorsque je suis trop emporté par les classes internes anonymes:

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

Ce sont toutes des classes qui ont été générées lorsque je réalisais une application simple et utilisaient un grand nombre de classes internes anonymes. Chaque classe sera compilée dans un fichier class distinct.

Comme on l'a déjà mentionné, "l'initialisation à double accolade" est une classe interne anonyme avec un bloc d'initialisation d'instance, ce qui signifie qu'une nouvelle classe est créée pour chaque "initialisation", dans le but généralement de créer un seul objet.

Étant donné que la machine virtuelle Java devra lire toutes ces classes lors de leur utilisation, le processus vérification par code intermédiaire peut prendre un certain temps. Sans oublier l'augmentation de l'espace disque nécessaire pour stocker tous ces fichiers class.

Il semble qu'il y ait un peu de frais généraux lors de l'utilisation de l'initialisation à double accolade; il n'est donc probablement pas une bonne idée de trop en faire. Mais comme Eddie l’a noté dans ses commentaires, il n’est pas possible d’être absolument sûr de l’impact.


Juste pour référence, l’initialisation à double accolade est la suivante:

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

Cela ressemble à une fonctionnalité "cachée" de Java, mais il ne s'agit que d'une réécriture de:

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

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

Il s’agit donc d’un bloc bloc d’initialisation d’instance qui fait partie d’une classe interne anonyme .


Joshua Bloch's proposition de collection Literals pour Project Coin était dans les 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 ni Java 7 ni 8 et a été mis de côté indéfiniment.


Expérience

Voici l'expérience simple que j'ai testée - créez 1000 ArrayLists avec les éléments "Hello" et "World!" ajoutés via la méthode add, en utilisant les deux méthodes suivantes:

Méthode 1: Initialisation à double attache

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

Méthode 2: Instanciez une 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 afin d'effectuer 1000 initialisations à l'aide des deux méthodes suivantes:

Test 1:

class Test1 {
  public static void main(String[] s) {
    long st = System.currentTimeMillis();

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

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

    /* snip */

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

    System.out.println(System.currentTimeMillis() - st);
  }
}

Test 2:

class Test2 {
  public static void main(String[] s) {
    long st = System.currentTimeMillis();

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

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

    /* snip */

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

    System.out.println(System.currentTimeMillis() - st);
  }
}

Veuillez noter que le temps écoulé pour initialiser les 1000 ArrayLists et les 1000 classes internes anonymes étendues ArrayList est vérifié à l'aide du System.currentTimeMillis, de sorte que le temporisateur n'a pas une résolution très élevée. Sur mon système Windows, la résolution est d'environ 15 à 16 millisecondes.

Les résultats pour 10 essais des deux tests sont 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 constater, l’initialisation à double attache a un temps d’exécution notable d’environ 190 ms.

Pendant ce temps, le temps d'exécution de l'initialisation ArrayList s'élevait à 0 ms. Bien entendu, la résolution du minuteur doit être prise en compte, mais elle risque d’être 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 en effet qu'il y ait une surcharge dans les deux méthodes d'initialisation.

Et oui, il y avait 1000 fichiers .class générés en compilant le programme de test d'initialisation Test1 à double accolade.

581
coobird

Une propriété de cette approche qui n’a pas été signalée jusqu’à présent est que, puisque vous créez des classes internes, la classe entière dans son ensemble est capturée. Cela signifie que tant que votre ensemble est actif, il conservera un pointeur sur l'instance conteneur (this$0) et l'empêchera d'être collecté, ce qui pourrait poser problème.

Ceci, et le fait qu'une nouvelle classe soit créée en premier lieu, même si un HashSet classique fonctionnerait très bien (ou même mieux), me donne envie de ne pas utiliser cette construction (même si je souhaite ardemment le sucre syntaxique).

Deuxième question: le nouveau HashSet doit être le "ceci" utilisé dans l'initialiseur d'instance ... Quelqu'un peut-il nous éclairer sur le mécanisme? Je m'attendais naïvement à ce que "ceci" fasse référence à l'objet initialisant les "saveurs".

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

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 en cours de création, vous pouvez également définir des méthodes. Par exemple, remplacer HashSet.add()

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

            @Override
            boolean add(String s){

            }

        };
    }
98
Thilo

Chaque fois que quelqu'un utilise l'initialisation à double attelle, un chaton est tué.

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

1. Vous créez trop de classes anonymes

Chaque fois que vous utilisez l'initialisation à double accolade, 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 cours:

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

C'est pas mal de frais généraux pour votre chargeur de classe - pour rien! Bien sûr, cela ne prendra pas beaucoup de temps d’initialisation si vous le faites une fois. Mais si vous faites cela 20 000 fois dans votre application d'entreprise ... toute cette mémoire mémoire pour un peu de "sucre syntaxique"?

2. Vous créez potentiellement une fuite de mémoire!

Si vous prenez le code ci-dessus et renvoyez cette carte à partir d'une méthode, les appelants de cette méthode risquent fort de garder des ressources très lourdes qui ne peuvent pas être récupéré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 Map retourné contiendra désormais une référence à l'instance englobante de ReallyHeavyObject. Vous ne voulez probablement pas risquer que:

Memory Leak Right Here

Image tirée de 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 littéraux de carte

Pour répondre à votre question, les gens ont utilisé cette syntaxe pour prétendre que Java ressemble à des littéraux de carte, similaires aux littéraux de tableau existants:

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

Certaines personnes peuvent trouver cela stimulant syntaxiquement.

47
Lukas Eder

sujet aux fuites

J'ai décidé d'intervenir. L'impact sur les performances inclut: utilisation du disque + unzip (pour le fichier jar), vérification de la classe, espace perm-gen (pour la JVM Hotspot de Sun). Cependant, le pire de tout: il est sujet aux fuites. Vous ne pouvez pas simplement revenir.

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

Donc, si l'ensemble s'échappe vers une autre partie chargée par un chargeur de classe différent et qu'une référence y est conservée, toute l'arborescence classes + chargeur de classe sera perdue. Pour éviter cela, une copie vers HashMap est nécessaire, new LinkedHashSet(new ArrayList(){{add("xxx);add("yyy");}}). Pas si mignon plus. Je n'utilise pas l'idiome, moi-même, c'est plutôt comme new LinkedHashSet(Arrays.asList("xxx","YYY"));

35
bestsss

Prendre la classe de test suivante:

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

puis en décompilant le fichier de classe, je vois:

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

Cela ne me semble pas terriblement inefficace. Si je m'inquiétais de la performance pour quelque chose comme ça, je la profilerais. Et le code ci-dessus répond à votre question n ° 2: vous êtes à l’intérieur d’un constructeur implicite (et d’un initialiseur) pour votre classe interne, donc "this" fait référence à cette classe interne.

Oui, cette syntaxe est obscure, mais un commentaire peut clarifier son utilisation. Pour clarifier la syntaxe, la plupart des gens connaissent un bloc d'initialisation statique (initialiseurs statiques JLS 8.7):

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'utilisation du constructeur (initialiseurs d'instance JLS 8.6), bien que je ne l'aie jamais vue utilisée dans le code de production. Ceci est beaucoup moins communément 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, alors le bloc de code entre { et } est transformé en constructeur par le compilateur. En gardant cela à l'esprit, découvrez le code de la double attache:

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

Le bloc de code situé entre les accolades les plus internes est transformé en constructeur par le compilateur. Les accolades les plus externes délimitent la classe interne anonyme. Pour en venir à la dernière étape de tout rendre 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");
    }
}

Pour les besoins de l'initialisation, je dirais qu'il n'y a pas de frais généraux (ou si petits que cela peut être négligé). Cependant, chaque utilisation de flavors n'ira pas contre HashSet, mais contre MyHashSet. Il y a probablement un petit surcoût (et peut-être même négligeable) à cela. Mais encore une fois, avant de m'en inquiéter, je le profilerais.

Encore une fois, pour votre question n ° 2, le code ci-dessus est l’équivalent logique et explicite de l’initialisation à double accolade, et il est évident que "this" se réfère: à la classe interne qui étend HashSet.

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

35
Eddie

Le chargement de nombreuses classes peut ajouter quelques millisecondes au début. Si le démarrage n'est pas si 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();
    }
}

empreintes

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
Peter Lawrey

Pour créer des ensembles, vous pouvez utiliser une méthode de fabrique varargs au lieu d'une initialisation à double accolade:

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

La bibliothèque Google Collections propose de nombreuses méthodes pratiques comme celle-ci, ainsi que de nombreuses autres fonctionnalités utiles.

En ce qui concerne l'obscurité du langage, je le rencontre et l'utilise tout le temps dans le code de production. Je serais plus préoccupé par les programmeurs qui sont déroutés par l'idiome autorisé à écrire du code de production.

16
Nat

En dehors de l'efficacité, je souhaite rarement créer une collection déclarative en dehors des tests unitaires. Je crois que la syntaxe à double accolade est très lisible.

Une autre façon de réaliser la construction déclarative de listes consiste à utiliser Arrays.asList(T ...) comme suit:

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

La limite de cette approche réside bien entendu dans le fait que vous ne pouvez pas contrôler le type spécifique de liste à générer.

9
Paul Morie

Il n'y a généralement rien de particulièrement inefficace à ce sujet. En règle générale, peu importe pour la machine virtuelle Java que vous ayez créé une sous-classe et y ait ajouté un constructeur - c'est une chose normale et quotidienne à faire dans un langage orienté objet. Je peux penser à des cas assez artificiels où vous pourriez causer une inefficacité en procédant de la sorte (par exemple, vous avez une méthode appelée plusieurs fois qui finit par prendre un mélange de différentes classes à cause de cette sous-classe, alors que la classe ordinaire transmise serait totalement prévisible- - dans ce dernier cas, le compilateur JIT pourrait effectuer des optimisations qui ne sont pas réalisables dans le premier cas). Mais en réalité, je pense que les cas où cela importera sont très artificiels.

Je verrais la question plus du point de vue de savoir si vous voulez "encombrer les choses" avec beaucoup de classes anonymes. À titre indicatif, envisagez d'utiliser l'idiome au maximum, par exemple des classes anonymes pour les gestionnaires d'événements.

Dans (2), vous êtes à l'intérieur du constructeur d'un objet, donc "ceci" fait référence à l'objet que vous construisez. Ce n'est pas différent de tout autre constructeur.

En ce qui concerne (3), cela dépend vraiment de qui gère votre code, je suppose. Si vous ne le savez pas à l'avance, un critère que je vous suggère d'utiliser est le suivant: "voyez-vous cela dans le code source du JDK?" (Dans ce cas, je ne me souviens pas d’avoir vu beaucoup d’initialiseurs anonymes, et certainement pas dans les cas où c’est le contenu niquement de la classe anonyme). Dans la plupart des projets de taille moyenne, je dirais que vous allez vraiment avoir besoin que vos programmeurs comprennent la source du JDK à un moment ou à un autre, de sorte que toute syntaxe ou idiome utilisé est un "jeu équitable". Au-delà de cela, je dirais, formez les gens sur cette syntaxe si vous avez le contrôle sur la personne qui gère le code, sinon commentez ou évitez.

7
Neil Coffey

L'initialisation à double accolade est un hack inutile qui peut engendrer des fuites de mémoire et d'autres problèmes

Il n'y a aucune raison légitime d'utiliser ce "truc". Guava fournit Nice collections immuables qui incluent à la fois des usines statiques et des constructeurs, ce qui vous permet de remplir votre collection là où elle est déclarée dans un environnement propre, lisible et sûr syntaxe.

L'exemple dans la question devient:

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

Non seulement cela est plus court et plus facile à lire, mais cela évite les nombreux problèmes liés au motif à double accolade décrit dans autres réponses . Bien sûr, il fonctionne de manière similaire à un HashMap construit directement, mais il est dangereux et sujet aux erreurs, et il existe de meilleures options.

Chaque fois que vous envisagez une initialisation à double haubanage, vous devez réexaminer vos API ou en introduire de nouvelles pour résoudre correctement le problème, plutôt que de tirer parti des astuces syntaxiques.

sujet aux erreurs maintenant marque cet anti-motif .

5
dimo414

Je faisais des recherches à ce sujet et 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 c'est ma conclusion

J'ai été surpris de constater que dans la plupart des tests d'exécution, l'initiation interne était en réalité plus rapide (presque le double dans certains cas). Lorsque vous travaillez avec de grands nombres, l'avantage semble disparaître.

Fait intéressant, le cas qui crée 3 objets sur la boucle perd son avantage se fait plus tôt que sur les autres cas. Je ne suis pas sûr de savoir pourquoi cela se produit et d’autres tests devraient être faits pour parvenir à des conclusions. La création d'implémentations concrètes peut aider à éviter le rechargement de la définition de classe (si c'est ce qui se passe)

Cependant, il est clair que peu de frais généraux ont été observés pour le seul bâtiment, même en cas de grand nombre.

Un inconvénient serait le fait que chacune des initiations à double attache crée un nouveau fichier de classe qui ajoute un bloc de disque entier à la taille de notre application (ou environ 1 ko une fois compressé). Un faible encombrement, mais s'il est utilisé dans de nombreux endroits, cela pourrait avoir un impact. Utilisez ces 1000 fois et vous ajoutez potentiellement un MiB entier à votre application, ce qui peut être préoccupant dans un environnement embarqué.

Ma conclusion? Il peut être bon d’utiliser tant qu’il n’est pas maltraité.

Laissez-moi savoir ce que vous pensez :)

4
pablisco

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 à partir d'asList (éléments):

static public Set<T> setOf(T ... elements) {
    Set set=new HashSet<T>(elements.size());
    for(T Elm: elements) { set.add(Elm); }
    return set;
    }
3
Lawrence Dol

Bien que cette syntaxe puisse être pratique, elle ajoute également beaucoup de ces références $ 0 car elles deviennent imbriquées et il est parfois difficile de déboguer dans les initialiseurs à moins que des points d'arrêt ne soient définis pour chacun d'eux. Pour cette raison, je recommande uniquement de l'utiliser pour les setters banals, en particulier les constantes, et les endroits où les sous-classes anonymes importent peu (par exemple, aucune sérialisation impliquée).

3
Eric Woodruff

Mario Gleichman décrit comment utiliser Java 1.5 fonctions génériques pour simuler Scala Liste des littéraux, bien que, malheureusement, vous vous retrouviez avec Listes immuables .

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 ainsi:

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

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

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

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

7/10/2014: Si seulement cela pouvait être aussi simple que Python:

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

3
Jim Ferrans
  1. Ceci appellera add() pour chaque membre. Si vous pouvez trouver un moyen plus efficace de placer des éléments dans un ensemble de hachages, utilisez-le. Notez que la classe interne va probablement générer des déchets, si vous êtes sensible à cela.

  2. Il me semble que le contexte est l’objet renvoyé par new, qui est le HashSet.

  3. Si vous devez demander ... Plus probablement: les personnes qui viendront après vous le sauront-elles ou non? Est-ce facile à comprendre et à expliquer? Si vous pouvez répondre "oui" aux deux, n'hésitez pas à l'utiliser.

2
Jon Watte