870 votes

Comment initialiser les valeurs de HashSet par construction ?

Je dois créer un Set avec des valeurs initiales.

Set<String> h = new HashSet<String>();
h.add("a");
h.add("b");

Existe-t-il un moyen de faire cela en une seule ligne de code ? Par exemple, c'est utile pour un champ statique final.

2 votes

Pour Java 8, Java 9 et Java 10, consultez ma réponse. stackoverflow.com/a/37406054/1216775

2 votes

La réponse de Michael Berdyshev est en fait la meilleure, car c'est la façon la plus propre de produire un modifiable Set. i_am_zero Answer dispose également d'une autre méthode pour créer un Set modifiable, mais elle est plus verbeuse/clastique [utilisation du streaming Lambda] ; sinon, i_am_zero Answer est le meilleur choix pour l'étendue de ses différentes options (à travers les versions de Java).

0 votes

NOTE : Certaines réponses omettent le paramètre `new HashSet<T>(int initialCapacity), si vous connaissez déjà la taille de la collection, utilisez-le.

1101voto

Gennadiy Points 3154

Il existe une sténographie que j'utilise et qui n'est pas très efficace en termes de temps, mais qui tient sur une seule ligne :

Set<String> h = new HashSet<>(Arrays.asList("a", "b"));

Encore une fois, cette méthode n'est pas efficace puisque vous construisez un tableau, le convertissez en une liste et utilisez cette liste pour créer un ensemble.

Lorsque j'initialise des ensembles finaux statiques, je l'écris généralement comme ceci :

public static final String[] SET_VALUES = new String[] { "a", "b" };
public static final Set<String> MY_SET = new HashSet<>(Arrays.asList(SET_VALUES));

Un peu moins laid et l'efficacité n'a pas d'importance pour l'initialisation statique.

2 votes

Bien qu'il soit très probablement correct au moment de l'écriture, à partir de Java 7, il faut utiliser l'opérateur Diamant comme dans : Set<String> h = new HashSet<>(Arrays.asList("a", "b")) ; public static final Set<String> MY_SET = new HashSet<>(Arrays.asList(SET_VALUES)) ;

0 votes

Corrigé pour la notation en diamant de Java comme vous l'avez mentionné, mais je n'ai pas eu le temps de le tester. Si quelqu'un peut faire une double vérification, ce serait génial. La principale préoccupation est l'affectation de hashset à set, le constructeur de HashSet peut avoir besoin d'un type générique explicite.

3 votes

@Gennadiy Je peux confirmer que la notation en diamant fonctionne bien (même à travers la déclaration Set et l'affectation HashSet).

393voto

Bozho Points 273663

Les littéraux de collection étaient prévus pour Java 7, mais n'y sont pas parvenus. Donc rien d'automatique pour le moment.

Vous pouvez utiliser la goyave Sets :

Sets.newHashSet("a", "b", "c")

Ou vous pouvez utiliser la syntaxe suivante, qui créera une classe anonyme, mais c'est un peu compliqué :

Set<String> h = new HashSet<String>() {{
    add("a");
    add("b");
}};

58 votes

@DD c'est un hack parce qu'il crée une sous-classe interne anonyme de HashSet avec une section d'initialisation statique qui appelle la méthode add(), ce qui est définitivement excessif pour cette situation. J'adore !

46 votes

L'initialisation par double accolade est un hack très dangereux. La classe anonyme a un lien implicite avec la classe externe, ce qui peut entraîner des fuites de mémoire. Celui qui détient l'ensemble initialisé par la double accolade détient également la classe dans laquelle l'ensemble a été créé.

7 votes

@fhucho La méthode est très populaire pour définir les attentes des objets fantaisie dans (disons) JMock, et elle a du sens dans ce cas, même à la lumière du risque de fuite de mémoire (les tests sont des processus de courte durée dont l'empreinte mémoire n'est pas importante).

222voto

En Java 8, j'utiliserais :

Set<String> set = Stream.of("a", "b").collect(Collectors.toSet());

Cela vous donne un mutable Set pré-initialisé avec "a" et "b". Notez que, bien que dans le JDK 8, cette méthode renvoie une valeur HashSet La spécification ne le garantit pas, et cela pourrait changer à l'avenir. Si vous souhaitez spécifiquement un HashSet faites ceci à la place :

Set<String> set = Stream.of("a", "b")
                        .collect(Collectors.toCollection(HashSet::new));

41 votes

C'est encore plus verbeux et inutilement complexe que le bon vieux code de la question initiale.

13 votes

@Natix Pour 2 éléments c'est plus verbeux, mais cette représentation commence à devenir concise si vous devez coder en dur l'ensemble avec plus d'éléments(10-100s)

7 votes

@Natix Oui, mais il est en ligne, dans certains cas c'est un avantage.

93voto

coobird Points 70356

Il y a plusieurs façons :

Initialisation de l'accolade double

Il s'agit d'une technique qui crée une classe interne anonyme dont l'initialisateur d'instance ajoute String à lui-même lorsqu'une instance est créée :

Set<String> s = new HashSet<String>() {{
    add("a");
    add("b");
}}

Gardez à l'esprit que cela créera en fait une nouvelle sous-classe de HashSet chaque fois qu'elle est utilisée, même si l'on ne doit pas écrire explicitement une nouvelle sous-classe.

Une méthode d'utilité

L'écriture d'une méthode qui renvoie un Set qui est initialisé avec les éléments désirés n'est pas trop difficile à écrire :

public static Set<String> newHashSet(String... strings) {
    HashSet<String> set = new HashSet<String>();

    for (String s : strings) {
        set.add(s);
    }
    return set;
}

Le code ci-dessus ne permet que l'utilisation d'une String mais il ne devrait pas être trop difficile de permettre l'utilisation de tout type en utilisant des génériques.

Utiliser une bibliothèque

De nombreuses bibliothèques disposent d'une méthode pratique pour initialiser les objets de la collection.

Par exemple, Collections Google a un Sets.newHashSet(T...) qui remplira un HashSet avec des éléments d'un type spécifique.

20 votes

GoogleCollections a également ImmutableSet.of(T...) . La plupart du temps, lorsque vous faites cela, vous n'avez pas besoin de modifier à nouveau l'ensemble plus tard, et dans ce cas, l'utilisation d'un ensemble immuable présente de nombreux avantages.

4 votes

Pour clarifier créera une nouvelle sous-classe de HashSet à chaque fois qu'elle sera utilisée. signifie "chaque fois qu'il est codé ". Chaque exécution fait pas créer une nouvelle sous-classe.

0 votes

L'initiation au double crochet peut être dangereuse si vous ne savez pas ce que vous faites, vérifiez les implications avant de l'utiliser.

31voto

Jason Nichols Points 5055

Vous pouvez le faire en Java 6 :

Set<String> h = new HashSet<String>(Arrays.asList("a", "b", "c"));

Mais pourquoi ? Je ne trouve pas que ce soit plus lisible que d'ajouter explicitement des éléments.

6 votes

Je le crée dans la déclaration. C'est une propriété finale. Merci

18 votes

Ah. Il est probablement utile de noter que vous pouvez toujours ajouter des éléments à un ensemble final. Déclarez simplement l'ensemble final, mais ajoutez des éléments dans le constructeur (ou n'importe où ailleurs) comme vous le feriez normalement. Des éléments peuvent être ajoutés ou supprimés dans une collection finale. Seule la référence à la collection elle-même est finale.

5 votes

+1 à ce que Jason Nichols a dit. N'oubliez pas d'utiliser Collections.unmodifiableSet(...).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X