web-dev-qa-db-fra.com

Comment initialiser les valeurs HashSet par construction?

J'ai besoin de créer un Set avec des valeurs initiales.

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

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

665
Serg

J'utilise un raccourci que je n'utilise pas très rapidement, mais qui correspond à une seule ligne:

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

Encore une fois, cela ne prend pas beaucoup de temps, car vous construisez un tableau, convertissez-le en liste et utilisez cette liste pour créer un ensemble.

Lors de l'initialisation 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 moche et l'efficacité n'a pas d'importance pour l'initialisation statique.

857
Gennadiy

Les littéraux de collection étaient planifiés pour Java 7, mais ne l'ont pas encore été insérés. Donc, rien n'est encore automatique.

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

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

Dans Java 8, je voudrais utiliser:

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 JDK 8, cela retourne un HashSet, la spécification ne le garantit pas, et cela pourrait changer dans le futur. Si vous voulez spécifiquement un HashSet, faites plutôt ceci:

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

Utilisation de Java 10 (ensembles non modifiables)

Set<String> strSet1 = Stream.of("A", "B", "C", "D")
         .collect(Collectors.toUnmodifiableSet());

Dans ce cas, le collecteur renverrait en fait l'ensemble non modifiable introduit dans Java 9, comme le montre l'instruction set -> (Set<T>)Set.of(set.toArray()) dans le code source.

Un point à noter est que la méthode Collections.unmodifiableSet() renvoie une vue non modifiable de l'ensemble spécifié (selon la documentation). Une collection de vues non modifiable est une collection qui est non modifiable et qui est également une vue sur une collection de sauvegarde. Notez que les modifications apportées à la collection de sauvegarde pourraient être toujours possibles et, si elles se produisent, elles sont visibles via la vue non modifiable. Mais la méthode Collectors.toUnmodifiableSet() renvoie réellement immuable définie dans Java 10 .


Utilisation de Java 9 (ensembles non modifiables)

Voici le moyen le plus compact d’initialiser un ensemble:

Set<String> strSet6 = Set.of("Apple", "Ball", "Cat", "Dog");

Nous avons 12 versions surchargées de cette méthode sine de commodité :

static <E> Set<E> of()

static <E> Set<E> of(E e1)

static <E> Set<E> of(E e1, E e2)

// ....etc

static <E> Set<E> of(E... elems)

Alors une question naturelle est pourquoi nous avons besoin de versions surchargées lorsque nous avons var-args ? La réponse est la suivante: chaque méthode var-arg crée un tableau en interne et le fait d’avoir les versions surchargées éviterait la création inutile d’objets et nous éviterait également la surcharge de la récupération de place.


Utilisation de Java 8 (ensembles modifiables)

Utilisation de Stream dans Java 8.

Set<String> strSet1 = Stream.of("A", "B", "C", "D")
         .collect(Collectors.toCollection(HashSet::new));

// stream from an array (String[] stringArray)
Set<String> strSet2 = Arrays.stream(stringArray)
         .collect(Collectors.toCollection(HashSet::new));

// stream from a list (List<String> stringList)
Set<String> strSet3 = stringList.stream()
         .collect(Collectors.toCollection(HashSet::new));

Utilisation de Java 8 (ensembles non modifiables)

Utilisation de Collections.unmodifiableSet ()

Nous pouvons utiliser Collections.unmodifiableSet() comme:

Set<String> strSet4 = Collections.unmodifiableSet(strSet1);

Mais cela semble un peu gênant et nous pouvons écrire notre propre collectionneur comme ceci:

class ImmutableCollector {
    public static <T> Collector<T, Set<T>, Set<T>> toImmutableSet() {
        return Collector.of(HashSet::new, Set::add, (l, r) -> {
            l.addAll(r);
            return l;
        }, Collections::unmodifiablSet);
    }
}

Et puis utilisez-le comme:

Set<String> strSet4 = Stream.of("A", "B", "C", "D")
             .collect(ImmutableCollector.toImmutableSet());


Utilisation de Collectors.collectingAndThen ()

Une autre approche consiste à utiliser la méthode Collectors.collectingAndThen() qui nous permet d’effectuer des transformations de finition supplémentaires:

import static Java.util.stream.Collectors.*;
Set<String> strSet5 = Stream.of("A", "B", "C", "D").collect(collectingAndThen(
   toCollection(HashSet::new),Collections::unmodifiableSet));

Si nous nous intéressons uniquement à Set, nous pouvons également utiliser Collectors.toSet() à la place de Collectors.toCollection(HashSet::new).

139
i_am_zero

Il y a plusieurs façons:

Initialisation à double accolade

C'est une technique qui crée une classe interne anonyme qui a un initialiseur d'instance qui ajoute Strings à elle-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 réalité une nouvelle sous-classe de HashSet à chaque utilisation, même s'il n'est pas nécessaire d'écrire explicitement une nouvelle sous-classe.

Une méthode utilitaire

Écrire une méthode qui retourne un Set initialisé avec les éléments souhaités n'est pas si 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 de String, mais il ne devrait pas être trop difficile de permettre l’utilisation de types utilisant des génériques.

Utiliser une bibliothèque

De nombreuses bibliothèques ont une méthode pratique pour initialiser les objets de collections.

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

87
coobird

Si vous n'avez qu'une seule valeur et que vous voulez obtenir un immuable, cela suffira:

Set<String> immutableSet = Collections.singleton("a");
37
Lu55

L’un des moyens les plus pratiques est l’utilisation de la méthode générique Collections.addAll () , qui utilise une collection et varargs:

Set<String> h = new HashSet<String>();
Collections.addAll(h, "a", "b");
27
Michael Berdyshev

Vous pouvez le faire dans Java 6:

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

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

27
Jason Nichols

Je pense que le plus lisible est simplement d’utiliser Google Guava:

Set<String> StringSet = Sets.newSet("a", "b", "c");
24
LanceP

Avec Java 9, vous pouvez effectuer les opérations suivantes:

Set.of("a", "b");

et vous obtiendrez un ensemble immuable contenant les éléments. Pour plus de détails, voir Oracle documentation de l'interface Set .

24
Mathias Bader

Une généralisation de la fonction d'utilité de la réponse de coobird pour la création de nouvelles HashSets:

public static <T> Set<T> newHashSet(T... objs) {
    Set<T> set = new HashSet<T>();
    for (T o : objs) {
        set.add(o);
    }
    return set;
}
14
Mark Elliot

Si le type contenu de l'ensemble est une énumération, il existe une méthode de fabrique Java (depuis la version 1.5):

Set<MY_ENUM> MY_SET = EnumSet.of( MY_ENUM.value1, MY_ENUM.value2, ... );
12
Heri

Avec Eclipse Collections , il existe différentes manières d’initialiser un Set contenant les caractères "a" et "b" dans une instruction. Eclipse Collections possède des conteneurs pour les types d'objet et de type primitif. J'ai donc expliqué comment utiliser un Set<String> ou CharSet en plus des versions mutable, immuable, synchronisée et non modifiable.

Set<String> set =
    Sets.mutable.with("a", "b");
HashSet<String> hashSet =
    Sets.mutable.with("a", "b").asLazy().into(new HashSet<String>());
Set<String> synchronizedSet =
    Sets.mutable.with("a", "b").asSynchronized();
Set<String> unmodifiableSet =
    Sets.mutable.with("a", "b").asUnmodifiable();

MutableSet<String> mutableSet =
    Sets.mutable.with("a", "b");
MutableSet<String> synchronizedMutableSet =
    Sets.mutable.with("a", "b").asSynchronized();
MutableSet<String> unmodifiableMutableSet =
    Sets.mutable.with("a", "b").asUnmodifiable();

ImmutableSet<String> immutableSet =
    Sets.immutable.with("a", "b");
ImmutableSet<String> immutableSet2 =
    Sets.mutable.with("a", "b").toImmutable();

CharSet charSet =
    CharSets.mutable.with('a', 'b');
CharSet synchronizedCharSet =
    CharSets.mutable.with('a', 'b').asSynchronized();
CharSet unmodifiableCharSet =
    CharSets.mutable.with('a', 'b').asUnmodifiable();
MutableCharSet mutableCharSet =
    CharSets.mutable.with('a', 'b');
ImmutableCharSet immutableCharSet =
    CharSets.immutable.with('a', 'b');
ImmutableCharSet immutableCharSet2 =
    CharSets.mutable.with('a', 'b').toImmutable();

Eclipse Collections est compatible avec Java 5 - 8.

Remarque: je suis un partisan des collections Eclipse.

5
Donald Raab
import com.google.common.collect.Sets;
Sets.newHashSet("a", "b");

ou

import com.google.common.collect.ImmutableSet;
ImmutableSet.of("a", "b");
5
Bryan Correll

(moche) Initialisation Double Brace sans effets secondaires:

Set<String> a = new HashSet<>(new HashSet<String>() {{
    add("1");
    add("2");
}})

Mais dans certains cas, si nous avons mentionné que c'est une bonne odeur de rendre les collections finales indiscutables, cela pourrait être vraiment utile:

final Set<String> a = Collections.unmodifiableSet(new HashSet<String>(){{
    add("1");
    add("2");
}})
4
egorlitvinenko

Juste une petite note, quelles que soient les approches délicates mentionnées ici, si vous utilisez un paramètre par défaut non modifié (comme un paramètre par défaut dans la bibliothèque que vous créez), il est judicieux de suivre ce modèle. :

// Initialize default values with the method you prefer, even in a static block
// It's a good idea to make sure these defaults aren't modifiable
private final static Set<String> DEFAULT_VALUES = Collections.unmodifiableSet(...);
private Set<String> values = DEFAULT_VALUES;

L'avantage dépend du nombre d'instances que vous créez pour cette classe et de la probabilité que les valeurs par défaut soient modifiées.

Si vous décidez de suivre ce modèle, vous aurez également la possibilité de choisir la méthode d’initialisation d’ensembles la plus lisible. Comme les micro-différences d'efficacité entre les différentes méthodes n'auront probablement pas beaucoup d'importance, vous n'initialiserez l'ensemble qu'une seule fois.

3
Amr Mostafa

Un peu compliqué mais fonctionne à partir de Java 5:

Set<String> h = new HashSet<String>(Arrays.asList(new String[] {  
    "a", "b"
}))

Utilisez une méthode d'assistance pour le rendre lisible:

Set<String> h = asSet ("a", "b");

public Set<String> asSet(String... values) {
    return new HashSet<String>(Java.util.Arrays.asList(values));
}
3
Aaron Digulla

En utilisant Java 8, nous pouvons créer HashSet comme:

Stream.of("A", "B", "C", "D").collect(Collectors.toCollection(HashSet::new));

Et si nous voulons un ensemble non modifiable, nous pouvons créer une méthode d’utilité telle que:

public static <T, A extends Set<T>> Collector<T, A, Set<T>> toImmutableSet(Supplier<A> supplier) {
        return Collector.of(
                supplier,
                Set::add, (left, right) -> {
                    left.addAll(right);
                    return left;
                }, Collections::unmodifiableSet);
    }

Cette méthode peut être utilisée comme:

 Stream.of("A", "B", "C", "D").collect(toImmutableSet(HashSet::new));
3
rakhi

Avec la sortie de Java9 et les méthodes de fabrique pratique , cela est possible de manière plus propre:

Set set = Set.of("a", "b", "c");
2
Naman

Peut utiliser un bloc statique pour l'initialisation:

private static Set<Integer> codes1=
        new HashSet<Integer>(Arrays.asList(1, 2, 3, 4));

private static Set<Integer> codes2 =
        new HashSet<Integer>(Arrays.asList(5, 6, 7, 8));

private static Set<Integer> h = new HashSet<Integer>();

static{
    h.add(codes1);
    h.add(codes2);
}
1
Ups

Combinaison de réponse par Michael Berdyshev avec Generics et utilisation du constructeur avec initialCapacity, comparaison avec Arrays.asList variant:

  import Java.util.Collections;
  import Java.util.HashSet;
  import Java.util.Set;

  @SafeVarargs
  public static <T> Set<T> buildSetModif(final T... values) {
    final Set<T> modifiableSet = new HashSet<T>(values.length);
    Collections.addAll(modifiableSet, values);
    return modifiableSet;
  }

  @SafeVarargs
  public static <T> Set<T> buildSetModifTypeSafe(final T... values) {
    return new HashSet<T>(Arrays.asList(values));
  }

  @SafeVarargs
  public static <T> Set<T> buildeSetUnmodif(final T... values) {
    return Collections.unmodifiableSet(buildSetModifTypeSafe(values));
    // Or use Set.of("a", "b", "c") if you use Java 9
  }
  • C'est bien si vous passez quelques valeurs pour init, pour tout ce qui est important, utilisez d'autres méthodes
  • Si vous mélangez accidentellement des types avec buildSetModif, le T résultant sera ? extends Object, ce qui n'est probablement pas ce que vous voulez, cela ne peut pas arriver avec la variante buildSetModifTypeSafe, ce qui signifie que buildSetModifTypeSafe(1, 2, "a"); ne pas compiler
0

C'est une solution élégante:

public static final <T> Set<T> makeSet(@SuppressWarnings("unchecked") T... o) {
        return new HashSet<T>() {
            private static final long serialVersionUID = -3634958843858172518L;
            {
                for (T x : o)
                   add(x);
            }
        };
}
0
ToxiCore

Le modèle Builder pourrait être utile ici. Aujourd'hui, j'ai eu le même problème. où j'avais besoin des opérations de mutation Set pour me renvoyer une référence de l'objet Set, afin que je puisse le transmettre au constructeur de la super classe afin qu'eux aussi puissent continuer à ajouter à ce même ensemble en construisant à son tour un nouveau StringSetBuilder à partir de Set que la classe enfant vient de construire. La classe de générateur que j'ai écrite ressemble à ceci (dans mon cas, il s'agit d'une classe interne statique d'une classe externe, mais il peut également s'agir de sa propre classe indépendante):

public interface Builder<T> {
    T build();
}

static class StringSetBuilder implements Builder<Set<String>> {
    private final Set<String> set = new HashSet<>();

    StringSetBuilder add(String pStr) {
        set.add(pStr);
        return this;
    }

    StringSetBuilder addAll(Set<String> pSet) {
        set.addAll(pSet);
        return this;
    }

    @Override
    public Set<String> build() {
        return set;
    }
}

Notez les méthodes addAll() et add(), qui sont définies en tant que contreparties de retour de Set.add() et Set.addAll(). Enfin, notez la méthode build(), qui renvoie une référence à l'ensemble que le générateur encapsule. Voici comment utiliser ce générateur Ensemble:

class SomeChildClass extends ParentClass {
    public SomeChildClass(String pStr) {
        super(new StringSetBuilder().add(pStr).build());
    }
}

class ParentClass {
    public ParentClass(Set<String> pSet) {
        super(new StringSetBuilder().addAll(pSet).add("my own str").build());
    }
}
0
Jose Quijada