Cum se inițializează valorile HashSet prin construcție? (Programare, Java, Colecții, Constructor, Inițializare, Hashset)

Serg a intrebat.
a intrebat.

Am nevoie să creez un Set cu valori inițiale.

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

Există o modalitate de a face acest lucru într-o singură linie de cod? De exemplu, este util pentru un câmp static final.

Comentarii

  • Pentru Java 8, Java 9 și Java 10 verificați răspunsul meu stackoverflow.com/a/37406054/1216775 –  > Por akhil_mittal.
  • Răspunsul lui Michael Berdyshev este de fapt cel mai bun, deoarece este cea mai curată modalitate de a produce un modificabil Set. i_am_zero Answer are, de asemenea, o modalitate diferită de a produce un Set modificabil, dar este mai verbos/mai greoi [folosind Lambda streaming]; în rest, i_am_zero Answer este următorul cel mai bun pentru amploarea opțiunilor sale diferite (în toate versiunile Java). –  > Por cellepo.
  • NOTĂ: Unele răspunsuri omit parametrul `new HashSet<T>(int initialCapacity), dacă știți deja dimensiunea colecției, utilizați-l. –  > Por Christophe Roussy.
24 răspunsuri
Gennadiy

Există o prescurtare pe care o folosesc eu, care nu este foarte eficientă din punct de vedere al timpului, dar care încape pe o singură linie:

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

Din nou, acest lucru nu este eficient din punct de vedere al timpului, deoarece construiți o matrice, o convertiți într-o listă și utilizați această listă pentru a crea un set.

Atunci când inițializez seturi statice finale, de obicei scriu astfel:

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

Un pic mai puțin urât și eficiența nu contează pentru inițializarea statică.

Comentarii

  • Deși cel mai probabil este corect la momentul scrierii acestui articol, începând cu Java 7 se utilizează operatorul Diamond ca în: Set<String> h = new HashSet<>(Arrays.asList(„a”, „b”)); public static final Set<String> MY_SET = new HashSet<>(Arrays.asList(SET_VALUES)); -.  > Por Nestor Milyaev.
  • Corectat pentru notația Java diamond, așa cum ați menționat, dar nu am avut timp să o testez. Dacă cineva poate verifica de două ori, ar fi minunat. Principala preocupare este atribuirea de la hashset la set, constructorul HashSet ar putea avea nevoie de un tip generic explicit. –  > Por Gennadiy.
  • @Gennadiy Pot confirma că notația de diamant funcționează bine (chiar și peste declarația Set și atribuirea HashSet.) – –  > Por Dan Temple.
  • @NestorMilyaev, Gennadiy ce ziceți de stream.of(…)? este aceasta o soluție mai bună cu java 8 prin preajmă? –  > Por Shilan.
  • 22

  • În Java 9: Set<String> h = Set.of("a", "b") //Unmodifiable Acest răspuns este actualizat –  > Por Mauve Ranger.
Bozho

Literații de colecție au fost programați pentru Java 7, dar nu au ajuns. Deci, nimic automat încă.

Puteți utiliza guava’s Sets:

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

Sau puteți folosi următoarea sintaxă, care va crea o clasă anonimă, dar este dificilă:

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

Comentarii

    53

  • @DD este un hack, deoarece creează o subclasă interioară anonimă a HashSet cu o secțiune de inițializare statică care apelează metoda add(), ceea ce este cu siguranță exagerat pentru această situație. Îmi place la nebunie! –  > Por Conan.
  • 42

  • Inițializarea cu bretele duble este un hack foarte periculos. Clasa anonimă are o legătură implicită cu clasa exterioară, ceea ce ar putea duce la scurgeri de memorie. Cel care deține setul inițializat cu dublă brățară deține și clasa în care a fost creat setul. –  > Por fhucho.
  • @fhucho Metoda este foarte populară pentru a seta așteptările obiectelor mock în (să zicem) JMock, și are sens în acest caz chiar și în lumina riscului de scurgere de memorie (testele sunt procese de scurtă durată a căror amprentă de memorie nu este importantă). –  > Por william.berg.

În Java 8 aș folosi:

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

Acest lucru vă oferă un fișier mutabil Set preinițializat cu „a” și „b”. Rețineți că, în timp ce în JDK 8 acest lucru returnează un HashSet, , specificația nu garantează acest lucru, iar acest lucru s-ar putea schimba în viitor. Dacă doriți în mod special un HashSet, faceți acest lucru în schimb:

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

Comentarii

    39

  • Acest lucru este chiar mai verbos și inutil de complex decât vechiul cod simplu din întrebarea inițială. –  > Por Natix.
  • @Natix Pentru 2 elemente este mai verbos, dar această reprezentare începe să devină concisă dacă trebuie să codificați dur setul cu mai multe elemente(10-100s) –  > Por Schleir.
  • @Natix Da, dar este inline, în unele cazuri acesta este un avantaj –  > Por deFreitas.
  • De ce nu versiunea mai compactă: Stream.of(1, 3, 5).collect(toSet()); Folosește import static java.util.stream.Collectors.toSet; – –  > Por borjab.
  • De asemenea, dacă doriți o Set cu un singur element, se poate crea un Singleton: final Set<T> SOME_SET = Collections.singleton(t); –  > Por Valentin Grégoire.
akhil_mittal

Dacă sunteți în căutarea celui mai compact mod de a inițializa un set, atunci acesta a fost în Java9:

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

===================== Răspuns detaliat mai jos =================================

Utilizarea Java 10 (Seturi nemodificabile)

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

Aici colectorul ar returna de fapt setul nemodificabil introdus în Java 9, după cum reiese din declarația set -> (Set<T>)Set.of(set.toArray()) din codul sursă.

Un aspect de reținut este că metoda Collections.unmodifiableSet() returnează o vizualizare nemodificabilă a setului specificat (conform documentației). Un vedere nemodificabilă este o colecție care este nemodificabilă și care este, de asemenea, o vizualizare a unei colecții suport. Rețineți că modificările aduse colecției suport pot fi modificate fi posibile, iar dacă acestea se produc, sunt vizibile prin intermediul vizualizării nemodificabile. Dar metoda Collectors.toUnmodifiableSet() returnează cu adevărat imuabil setat în Java 10.


Utilizarea Java 9 (Seturi nemodificabile)

Următorul este cel mai compact cel mai compact mod de a inițializa un set:

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

Avem 12 versiuni supraîncărcate ale acestui fabrică convenabilă de tip factory:

static <E> Set<E> of()

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

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

// …. și așa mai departe.

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

Atunci o întrebare firească este de ce avem nevoie de versiuni supraîncărcate atunci când avem var-arg-uri? Răspunsul este următorul: fiecare metodă var-arg creează un array intern, iar existența versiunilor supraîncărcate ar evita crearea inutilă a unui obiect și ne-ar scuti, de asemenea, de colectarea gunoiului.


Utilizarea Java 8 (seturi modificabile)

Utilizarea Stream în 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));

Utilizarea Java 8 (seturi nemodificabile)

Utilizarea Collections.unmodifiableSet()

Putem utiliza Collections.unmodifiableSet() ca:

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

Dar arată ușor ciudat și putem scrie propriul colector astfel::

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

Și apoi să-l folosim ca:

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

### Using Collectors.collectingAndThen()O altă abordare este să folosim metoda [`Collectors.collectingAndThen()`][6] care ne permite să efectuăm transformări suplimentare de finisare:

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

Dacă ne pasă doar de Set atunci putem utiliza și Collectors.toSet() în loc de Collectors.toCollection(HashSet::new).

Comentarii

  • Noroc pentru secțiunea „de ce avem nevoie de versiuni supraîncărcate când avem var-arg?”  > Por Daniel Pop.
coobird

Există câteva modalități:

Inițializarea cu bretele duble

Aceasta este o tehnică care creează o clasă interioară anonimă care are un inițializator de instanță care adaugă Strings la el însuși atunci când este creată o instanță:

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

Rețineți că acest lucru va crea de fapt o nouă subclasă de HashSet de fiecare dată când este utilizată, chiar dacă nu trebuie să se scrie explicit o nouă subclasă.

O metodă utilitară

Scrierea unei metode care returnează un Set care este inițializat cu elementele dorite nu este prea greu de scris:

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

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

Codul de mai sus nu permite decât o utilizare a lui a String, , dar nu ar trebui să fie prea dificil să se permită utilizarea oricărui tip folosind genericele.

Utilizați o bibliotecă

Multe biblioteci au o metodă de comoditate pentru a inițializa obiectele colecțiilor.

De exemplu, Google Collections are o metodă Sets.newHashSet(T...) care va popula un obiect HashSet cu elemente de un anumit tip.

Comentarii

    20

  • De asemenea, GoogleCollections are ImmutableSet.of(T...). De cele mai multe ori, atunci când faceți acest lucru, nu trebuie să modificați din nou setul mai târziu și, în acest caz, utilizarea unui set imuabil are multe avantaje. –  > Por Kevin Bourrillion.
  • Pentru a clarifica va crea o nouă subclasă de HashSet de fiecare dată când este utilizat, , înseamnă „de fiecare dată când este codat„. Fiecare execuție face nu creează o nouă subclasă. –  > Por Bohemian.
  • Inițierea dublei paranteze poate fi periculoasă dacă nu știți ce faceți, căutați implicațiile înainte de a o folosi.  > Por Amalgovinus.
Lu55

Dacă aveți doar o singură valoare și doriți să obțineți un imuabilă acest lucru ar fi suficient:

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

Comentarii

  • Acest lucru a fost foarte util deoarece instrumentul nostru de analiză a codului se plânge dacă folosiți new HashSet<>(Arrays.asList( _values_ )) cu o singură valoare. –  > Por yokeho.
  • Atenție, Set<String> rezultat este imuabil. – user1706698
Michael Berdyshev

Una dintre cele mai convenabile modalități este utilizarea genericului Collections.addAll() care primește o colecție și varargs:

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

Comentarii

  • Vă mulțumim! Mă bucur că am citit mai jos, imo acest lucru ar trebui să fie clasat mai sus. Acest lucru este minunat pentru Java 8 și mai puțin; fără copiere în jurul array-urilor și fluxuri neîndemânatice. Vă mulțumim! –  > Por redeveloper.
  • Acesta este un răspuns grozav, deoarece, spre deosebire de multe alte răspunsuri, din câte îmi dau seama, oferă un modificabil Set 😉 –  > Por cellepo.
  • Cred că acest lucru este disponibil cel puțin în Java 5+. –  > Por cellepo.
Mathias Bader

Cu Java 9 puteți face următoarele:

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

și veți obține un Set imuabil care conține elementele. Pentru detalii, consultați pagina de internet Oracle documentația Oracle a interfeței Set.

Jason Nichols

Puteți face acest lucru în Java 6:

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

Dar de ce? Nu mi se pare mai ușor de citit decât adăugarea explicită a elementelor.

Comentarii

  • Îl creez în declarație. Este o proprietate finală. Mulțumesc.  > Por Serg.
  • 18

  • Ah. Probabil că merită să rețineți că puteți adăuga în continuare elemente la un set final. Pur și simplu declarați setul final, dar adăugați elemente în constructor (sau oriunde altundeva) așa cum ați face-o în mod normal. O colecție finală poate avea în continuare elemente adăugate sau eliminate. Doar referința la colecția în sine este finală. –  > Por Jason Nichols.
  • +1 la ceea ce a spus Jason Nichols. Nu uitați să folosiți Collections.unmodifiableSet(…). –  > Por Eli Acherkan.
  • Adică, ar fi mai ușor de citit dacă ar fi fost new HashSet<String>("a", "b", "c"); –  > Por sam boosalis.
  • Rețineți că Arrays#asList returnează o listă imuabilă din punct de vedere structural –  > Por cellepo.
LanceP

Cred că cel mai ușor de citit este să folosiți pur și simplu google Guava:

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

Comentarii

  • Aceasta este acum Sets.newHashSet(). –  > Por membersound.
  • @membersound din ce pachet ar trebui să import pentru a folosi Sets.newHashSet() ? –  > Por Abdennacer Lachiheb.
  • @AbdennacerLachiheb „com.google.common.collect.Sets” – –  > Por sachintha hewawasam.
Mark Elliot

O generalizare a funcției de utilitate a răspunsului lui coobird pentru crearea de noi HashSets:

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

Comentarii

  • init with size please + add null check –  > Por Spektakulatius.
Heri

Dacă tipul conținut de Set este o enumerare, atunci există o metodă de fabrică construită în java (începând cu versiunea 1.5):

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

Avery Michelle Dawn
import com.google.common.collect.Sets;
Sets.newHashSet("a", "b");

sau

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

Donald Raab

Cu Eclipse Collections există câteva moduri diferite de a inițializa o colecție Set care conține caracterele „a” și „b” într-o singură instrucțiune. Eclipse Collections dispune de containere atât pentru obiecte, cât și pentru tipuri primitive, așa că am ilustrat cum ați putea folosi a Set<String> sau CharSet pe lângă versiunile mutabile, imuabile, sincronizate și nemodificabile ale ambelor.

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

Notă: Sunt un committer pentru Eclipse Collections.

egorlitvinenko

(urât) Inițializare Double Brace fără efecte secundare:

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

Dar, în unele cazuri, dacă am menționat că este un miros bun pentru a face colecțiile finale nemutabile, ar putea fi foarte util:

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

Aaron Digulla

Un pic întortocheat, dar funcționează de la Java 5:

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

Utilizați o metodă de ajutor pentru a o face lizibilă:

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

public Set<String> asSet(String... values) {
    return new HashSet<String>(java.util.Arrays.asList(values));
}

Comentarii

  • (Nu ați avut nevoie de new String[] {) –  > Por Kevin Bourrillion.
Amr Mostafa

Doar o mică observație, indiferent de care dintre abordările frumoase menționate aici veți alege, dacă este vorba de o valoare implicită care de obicei nu este modificată (cum ar fi o setare implicită într-o bibliotecă pe care o creați), este o idee bună să urmați acest model:

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

Beneficiul depinde de numărul de instanțe pe care le creați pentru clasa respectivă și de cât de probabil este ca valorile implicite să fie modificate.

Dacă decideți să urmați acest model, atunci puteți alege și metoda de inițializare a seturilor care este cea mai ușor de citit. Deoarece microdiferențele de eficiență dintre diferitele metode nu vor conta probabil prea mult, deoarece veți inițializa setul o singură dată.

rakhi

Utilizarea Java 8 putem crea HashSet ca:

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

Și dacă dorim un set nemodificabil putem crea o metodă utilitară ca :

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

Această metodă poate fi utilizată ca :

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

Naman

Odată cu lansarea și metodele convenabile de fabrică, acest lucru este posibil într-un mod mai curat:

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

Comentarii

  • @cellepo Ei bine, istoricul de editare al acestui răspuns sugerează că a fost editat mai târziu. Deși de acord, conținutul este același și acel răspuns acoperă mult mai multe modalități de a rezolva întrebarea. –  > Por Naman.
Ups

Se poate folosi un bloc static pentru inițializare:

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

ToxiCore

Aceasta este o soluție elegantă:

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

Comentarii

  • serialVersionUID este o idee incredibil de proastă în toate cazurile –  > Por Inginer de software.
Jose Quijada

Modelul Builder ar putea fi de folos aici. Azi am avut aceeași problemă. unde aveam nevoie ca operațiile de mutare a setului să îmi returneze o referință a obiectului Set, astfel încât să o pot transmite constructorului clasei super, astfel încât și acesta să poată continua să adauge la același set, construind la rândul său un nou StringSetBuilder din Setul pe care clasa copil tocmai l-a construit. Clasa constructor pe care am scris-o arată astfel (în cazul meu este o clasă statică interioară a unei clase exterioare, dar poate fi și o clasă proprie independentă):

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

Observați că addAll() și add() care sunt omologii Set care returnează seturile Set.add() și Set.addAll(). În cele din urmă, observați metoda build() care returnează o referință la setul pe care constructorul îl încapsulează. Mai jos este ilustrat modul de utilizare a acestui constructor de seturi:

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

Christophe Roussy

Combinarea răspunsului prin Michael Berdyshev cu Generics și utilizarea constructorului cu initialCapacity, comparând cu Arrays.asList varianta:

  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
  }
  • Acest lucru este bun dacă treceți câteva valori pentru init, pentru ceva mareutilizați alte metode
  • Dacă amestecați din greșeală tipurile cu buildSetModif T rezultat va fi ? extends Object, , ceea ce probabil nu este ceea ce doriți, acest lucru nu se poate întâmpla cu metoda buildSetModifTypeSafe varianta, ceea ce înseamnă că buildSetModifTypeSafe(1, 2, "a"); nu se va compila

Def_Os

De asemenea, puteți utiliza vavr:

import io.vavr.collection.HashSet;

HashSet.of("a", "b").toJavaSet();