wake-up-neo.com

Wie initialisiere ich HashSet-Werte durch Konstruktion?

Ich muss ein Set mit Anfangswerten erstellen.

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

Gibt es eine Möglichkeit, dies in einer Codezeile zu tun? Zum Beispiel ist es nützlich für ein endgültiges statisches Feld.

665
Serg

Ich verwende eine Kurzform, die nicht sehr zeiteffizient ist, aber in eine einzelne Zeile passt:

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

Auch dies ist nicht zeiteffizient, da Sie ein Array erstellen, in eine Liste konvertieren und diese Liste zum Erstellen eines Sets verwenden.

Wenn ich statische Final Sets initialisiere, schreibe ich es normalerweise so:

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

Etwas weniger hässlich und die Effizienz spielt für die statische Initialisierung keine Rolle.

857
Gennadiy

Sammlungsliterale waren für Java 7 geplant, haben es aber nicht geschafft. Also noch nichts Automatisches.

Du kannst guava's Sets benutzen:

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

Sie können auch die folgende Syntax verwenden, mit der eine anonyme Klasse erstellt wird, die jedoch verrückt ist:

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

In Java 8 würde ich verwenden:

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

Dies gibt Ihnen eine veränderbare Set mit "a" und "b" vorinitialisiert. Beachten Sie, dass in JDK 8 zwar HashSet zurückgegeben wird, die Spezifikation dies jedoch nicht garantiert. Dies kann sich in Zukunft ändern. Wenn Sie speziell ein HashSet möchten, gehen Sie stattdessen folgendermaßen vor:

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

Verwenden von Java 10 (nicht änderbare Mengen)

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

Hier würde der Collector tatsächlich die in Java 9 eingeführte nicht modifizierbare Menge zurückgeben, wie aus der Anweisung set -> (Set<T>)Set.of(set.toArray()) im Quellcode hervorgeht.

Ein zu beachtender Punkt ist, dass die Methode Collections.unmodifiableSet() eine nicht änderbare Ansicht des angegebenen Satzes (gemäß Dokumentation) zurückgibt. Eine nicht modifizierbare Ansichtssammlung ist eine Sammlung, die nicht modifizierbar ist und auch eine Ansicht auf eine Hintergrundsammlung darstellt. Beachten Sie, dass Änderungen an der Backing-Auflistung möglicherweise noch möglich sind . Wenn sie auftreten, sind sie in der unveränderlichen Ansicht sichtbar. Aber die Methode Collectors.toUnmodifiableSet() gibt wirklich unveränderliches zurück, das in Java 10 eingestellt ist.


Verwenden von Java 9 (nicht änderbare Mengen)

Das Folgende ist die kompakteste Art, einen Satz zu initialisieren:

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

Wir haben 12 überladene Versionen dieser Convenience Factory Methode:

static <E> Set<E> of()

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

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

// ....und so weiter

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

Dann ist eine natürliche Frage , warum wir überladene Versionen benötigen, wenn wir var-args haben. Die Antwort lautet: Jede var-arg-Methode erstellt intern ein Array und eine überladene Version vermeidet unnötige Objekterstellung und erspart uns außerdem den Speicherbereinigungsaufwand.


Verwenden von Java 8 (Modifizierbare Mengen)

Verwenden von Stream in 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));

Verwenden von Java 8 (nicht änderbare Mengen)

Collections.unmodifiableSet () verwenden

Wir können Collections.unmodifiableSet() verwenden als:

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

Aber es sieht etwas umständlich aus und wir können unseren eigenen Sammler so schreiben:

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

Und dann benutze es als:

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


Collectors.collectingAndThen () verwenden

Ein anderer Ansatz ist die Verwendung der Methode Collectors.collectingAndThen() , mit der wir zusätzliche Finishing-Transformationen durchführen können:

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

Wenn wir uns nur um Set kümmern, können wir auch Collectors.toSet() anstelle von Collectors.toCollection(HashSet::new) verwenden.

139
i_am_zero

Es gibt einige Möglichkeiten:

Doppelte geschweifte Klammer

Dies ist eine Technik, die eine anonyme innere Klasse mit einem Instanzinitialisierer erstellt, der sich beim Erstellen einer Instanz Strings hinzufügt:

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

Beachten Sie, dass dadurch bei jeder Verwendung eine neue Unterklasse von HashSet erstellt wird, obwohl nicht explizit eine neue Unterklasse erstellt werden muss.

Eine Dienstprogrammmethode

Das Schreiben einer Methode, die ein Set zurückgibt, das mit den gewünschten Elementen initialisiert ist, ist nicht allzu schwer zu schreiben:

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

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

Der obige Code erlaubt nur die Verwendung von String, es sollte jedoch nicht zu schwierig sein, die Verwendung eines beliebigen Typs unter Verwendung von Generika zuzulassen.

Verwenden Sie eine Bibliothek

Viele Bibliotheken verfügen über eine bequeme Methode zum Initialisieren von Sammlungsobjekten.

Zum Beispiel hat Google Collections eine Sets.newHashSet(T...) -Methode, die eine HashSet mit Elementen eines bestimmten Typs auffüllt.

87
coobird

Wenn Sie nur einen Wert haben und ein nveränderliches setzen möchten, reicht dies aus:

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

Eine der bequemsten Möglichkeiten ist die Verwendung der generischen Collections.addAll () -Methode, die eine Sammlung und varargs akzeptiert:

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

Sie können dies in Java 6 tun:

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

Aber wieso? Ich finde es nicht lesbarer als das explizite Hinzufügen von Elementen.

27
Jason Nichols

Ich bin der Meinung, dass es am besten lesbar ist, einfach Google Guava zu verwenden:

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

Mit Java 9 können Sie Folgendes tun:

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

und du bekommst ein unveränderliches Set mit den Elementen. Einzelheiten finden Sie in Oracle Dokumentation von Interface Set .

24
Mathias Bader

Eine Verallgemeinerung der Hilfsprogrammfunktion coobirds Antwort zum Erstellen neuer 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

Wenn der enthaltene Typ des Sets eine Aufzählung ist, gibt es eine Java eingebaute Factory-Methode (seit 1.5):

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

Mit Eclipse Collections gibt es verschiedene Möglichkeiten, einen Set zu initialisieren, der die Zeichen 'a' und 'b' in einer Anweisung enthält. Eclipse Collections enthält Container für Objekt- und primitive Typen. Daher habe ich gezeigt, wie Sie neben veränderlichen, unveränderlichen, synchronisierten und nicht veränderbaren Versionen von beiden auch Set<String> oder CharSet verwenden können.

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 ist kompatibel mit Java 5 - 8.

Hinweis: Ich bin ein Committer für Eclipse-Sammlungen.

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

oder

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

(hässliche) Double Brace Initialisierung ohne Nebenwirkungen:

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

Aber in einigen Fällen könnte es wirklich nützlich sein, wenn wir erwähnen, dass dies ein guter Geruch ist, um endgültige Sammlungen unveränderlich zu machen:

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

Nur eine kleine Anmerkung: Unabhängig davon, mit welchen der hier genannten Vorgehensweisen Sie am Ende fertig sind, ist es eine gute Idee, diesem Muster zu folgen, wenn dies eine Standardeinstellung ist, die normalerweise unverändert bleibt (wie eine Standardeinstellung in einer Bibliothek, die Sie erstellen) :

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

Der Vorteil hängt von der Anzahl der Instanzen ab, die Sie für diese Klasse erstellen, und davon, wie wahrscheinlich es ist, dass die Standardeinstellungen geändert werden.

Wenn Sie sich für dieses Muster entscheiden, können Sie auch die am besten lesbare Methode für die Satzinitialisierung auswählen. Da die mikroskopischen Unterschiede in der Effizienz zwischen den verschiedenen Methoden wahrscheinlich keine große Rolle spielen, werden Sie den Satz nur einmal initialisieren.

3
Amr Mostafa

Ein bisschen verschlungen, funktioniert aber ab Java 5:

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

Verwenden Sie eine Hilfsmethode, um es lesbar zu machen:

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

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

Mit Java 8 können wir HashSet erstellen als:

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

Und wenn wir eine nicht modifizierbare Menge wollen, können wir eine Utility-Methode erstellen als:

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

Diese Methode kann verwendet werden als:

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

Mit der Veröffentlichung von Java9 und Convenience-Factory-Methoden ist dies auf eine sauberere Art und Weise möglich:

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

Kann statischen Block zur Initialisierung verwenden:

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

Kombiniere Antwort von Michael Berdyshev mit Generics und benutze Konstruktor mit initialCapacity, vergleiche mit Arrays.asList Variante:

  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
  }
  • Dies ist gut, wenn Sie ein paar Werte für init übergeben, für alles, was groß ist, andere Methoden verwenden
  • Wenn Sie versehentlich Typen mit buildSetModif mischen, ist das resultierende T ? extends Object, was wahrscheinlich nicht das ist, was Sie wollen. Dies kann bei der buildSetModifTypeSafe -Variante nicht passieren, was bedeutet, dass buildSetModifTypeSafe(1, 2, "a"); wird nicht kompilieren
0

Dies ist eine elegante Lösung:

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

Hier kann das Builder-Muster hilfreich sein. Heute hatte ich das gleiche Problem. Dort, wo Set-Mutationsoperationen erforderlich waren, um mir eine Referenz des Set-Objekts zurückzugeben, kann diese an den Superklassenkonstruktor übergeben werden, damit auch diese weiterhin dieselbe Menge hinzufügen können, indem wiederum ein neuer StringSetBuilder aus der Menge der untergeordneten Klasse erstellt wird gerade gebaut. Die Builder-Klasse, die ich geschrieben habe, sieht folgendermaßen aus (in meinem Fall ist es eine statische innere Klasse einer äußeren Klasse, aber es kann auch eine eigene unabhängige Klasse sein):

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

Beachten Sie die Methoden addAll() und add(), bei denen es sich um zurückgegebene Entsprechungen von Set.add() und Set.addAll() handelt. Beachten Sie abschließend die Methode build(), die einen Verweis auf das vom Builder gekapselte Set zurückgibt. Unten sehen Sie, wie Sie diesen Builder für Mengen verwenden:

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