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.
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.
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");
}};
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));
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 .
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 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));
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());
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)
.
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 String
s à 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.
Si vous n'avez qu'une seule valeur et que vous voulez obtenir un immuable, cela suffira:
Set<String> immutableSet = Collections.singleton("a");
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");
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.
Je pense que le plus lisible est simplement d’utiliser Google Guava:
Set<String> StringSet = Sets.newSet("a", "b", "c");
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 .
Une généralisation de la fonction d'utilité de la réponse de coobird pour la création de nouvelles HashSet
s:
public static <T> Set<T> newHashSet(T... objs) {
Set<T> set = new HashSet<T>();
for (T o : objs) {
set.add(o);
}
return set;
}
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, ... );
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.
import com.google.common.collect.Sets;
Sets.newHashSet("a", "b");
ou
import com.google.common.collect.ImmutableSet;
ImmutableSet.of("a", "b");
(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");
}})
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.
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));
}
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));
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");
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);
}
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
}
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 compilerC'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);
}
};
}
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());
}
}