Как инициализировать значения HashSet по конструкции?

Мне нужно создать Set с начальными значениями.

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

Есть ли способ сделать это одной строкой кода? Например, это полезно для финального статического поля.


person Serg    schedule 11.01.2010    source источник
comment
Для Java 8, Java 9 и Java 10 проверьте мой ответ stackoverflow.com/a/37406054/1216775   -  person akhil_mittal    schedule 10.08.2018
comment
Ответ Михаила Бердышева на самом деле лучший, потому что это самый чистый способ создания изменяемого набора. В ответе i_am_zero также есть другой способ создания модифицируемого набора, но он более подробный / неуклюжий [с использованием потоковой передачи Lambda]; в противном случае ответ i_am_zero является следующим лучшим по широте его различных вариантов (в разных версиях Java).   -  person cellepo    schedule 30.01.2019
comment
ПРИМЕЧАНИЕ. В некоторых ответах опускается параметр `new HashSet ‹T› (int initialCapacity), если вы уже знаете размер коллекции, используя его.   -  person Christophe Roussy    schedule 27.05.2019


Ответы (23)


Я использую сокращенную запись, которая не очень эффективна по времени, но умещается в одной строке:

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

Опять же, это неэффективно по времени, поскольку вы создаете массив, преобразуете его в список и используете этот список для создания набора.

При инициализации статических финальных наборов я обычно пишу так:

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

Немного менее уродливый и эффективность не имеет значения для статической инициализации.

person Gennadiy    schedule 11.01.2010
comment
Хотя, скорее всего, это правильно на момент написания, в Java 7 действительно используется оператор Diamond, как в: Set ‹String› h = new HashSet ‹› (Arrays.asList (a, b)); общедоступный статический окончательный Set ‹String› MY_SET = new HashSet ‹› (Arrays.asList (SET_VALUES)); - person Nestor Milyaev; 18.05.2017
comment
Как вы упомянули, исправлено для обозначения ромбов Java, но у него не было времени проверить это. Если бы кто-нибудь мог перепроверить, это было бы круто. Основная проблема - это хэш-набор для установки назначения, конструктору HashSet может потребоваться явный универсальный тип. - person Gennadiy; 25.07.2017
comment
@Gennadiy Может подтвердить, что ромбовидная нотация работает нормально (даже при объявлении Set и назначении HashSet.) - person Dan Temple; 07.08.2017
comment
@NestorMilyaev, Геннадий как насчет stream.of (...)? это лучшее решение с Java 8? - person Shilan; 05.04.2018
comment
В Java 9: ​​Set<String> h = Set.of("a", "b") //Unmodifiable Этот ответ актуален - person Mauve Ranger; 02.07.2018
comment
Обратите внимание, что Arrays # asList возвращает структурно неизменяемый список - person cellepo; 30.01.2019
comment
См. Ответ Михаила Бердышева о [пока] самом чистом способе создания изменяемого набора. - person cellepo; 30.01.2019

Литералы коллекции были запланированы для Java 7, но не вошли. Так что пока ничего автоматического.

Вы можете использовать _1 _ :

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

Или вы можете использовать следующий синтаксис, который создаст анонимный класс, но он хакерский:

Set<String> h = new HashSet<String>() {{
    add("a");
    add("b");
}};
person Bozho    schedule 11.01.2010
comment
@DD это взлом, потому что он создает анонимный внутренний подкласс HashSet со статической секцией инициализации, которая вызывает метод add (), что определенно является излишним для этой ситуации. Я люблю это! - person Conan; 12.07.2012
comment
Инициализация двойной скобки - очень опасный взлом. Анонимный класс имеет неявную ссылку на внешний класс, что может привести к утечке памяти. Тот, кто держит инициализированный двойными скобками набор, также владеет классом, в котором этот набор был создан. - person fhucho; 24.12.2012
comment
@fhucho Этот метод очень популярен для установки ожиданий фиктивного объекта в (скажем) JMock, и в этом случае он имеет смысл даже в свете риска утечки памяти (тесты - это недолговечные процессы, объем памяти которых не важен). - person william.berg; 05.03.2013

Если вы ищете наиболее компактный способ инициализации набора, то это было в Java9:

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

===================== Подробный ответ ниже ========================== =======

Использование Java 10 (неизменяемые наборы)

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

Здесь сборщик фактически вернет неизменяемый набор, представленный в Java 9, как видно из оператора set -> (Set<T>)Set.of(set.toArray()) в исходном коде.

Следует отметить, что метод _ 5_ возвращает неизменяемое представление указанного набора (согласно документации). Коллекция неизменяемых представлений - это неизменяемая коллекция, которая также является представлением для вспомогательной коллекции. Обратите внимание, что изменения в фоновой коллекции могут по-прежнему возможны, и если они происходят, они видны в неизменяемом представлении. Но метод _6 _ возвращает действительно неизменяемый, установленный в Java 10.


Использование Java 9 (неизменяемые наборы)

Ниже приводится самый компактный способ инициализации набора:

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

У нас есть 12 перегруженных версий этого метода удобной фабрики:

static <E> Set<E> of()

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

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

// ....и так далее

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

Тогда возникает естественный вопрос: зачем нам перегруженные версии, когда у нас есть аргументы var-args? Ответ таков: каждый метод var-arg создает массив внутри, и наличие перегруженных версий позволит избежать ненужного создания объекта, а также избавит нас от накладных расходов на сборку мусора.


Использование Java 8 (изменяемые наборы)

Использование Stream в 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));

Использование Java 8 (неизменяемые наборы)

Использование Collections.unmodifiableSet ()

Мы можем использовать Collections.unmodifiableSet() как:

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

Но это выглядит немного неудобно, и мы можем написать собственный сборщик вот так:

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

А затем используйте его как:

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

### Using Collectors.collectingAndThen() Another approach is to use the method [`Collectors.collectingAndThen()`][6] which lets us perform additional finishing transformations:
import static java.util.stream.Collectors.*;
Set<String> strSet5 = Stream.of("A", "B", "C", "D").collect(collectingAndThen(
   toCollection(HashSet::new),Collections::unmodifiableSet));

Если нас интересует только Set, мы также можем использовать _ 19_ вместо Collectors.toCollection(HashSet::new).

person akhil_mittal    schedule 24.05.2016
comment
Ура за то, зачем нам нужны перегруженные версии, когда у нас есть аргументы var-args? раздел - person Daniel Pop; 31.08.2020

В Java 8 я бы использовал:

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

Это дает вам изменяемый Set, предварительно инициализированный "a" и "b". Обратите внимание, что хотя в JDK 8 это действительно возвращает HashSet, спецификация не гарантирует этого, и это может измениться в будущем. Если вам конкретно нужен HashSet, сделайте это:

Set<String> set = Stream.of("a", "b")
                        .collect(Collectors.toCollection(HashSet::new));
person Community    schedule 13.01.2014
comment
Это даже более многословно и излишне сложно, чем простой старый код в исходном вопросе. - person Natix; 06.09.2016
comment
@Natix Для двух элементов он более подробный, но это представление начинает становиться кратким, если вам нужно жестко закодировать набор с большим количеством элементов (10-100 с) - person Schleir; 12.07.2017
comment
@Natix Да, но он встроенный, в некоторых случаях это преимущество - person deFreitas; 22.04.2018
comment
Почему не более компактная версия: Stream.of (1, 3, 5) .collect (toSet ()); Он использует import static java.util.stream.Collectors.toSet; - person borjab; 05.09.2018
comment
ТАКЖЕ, если вам нужен Set только с одним элементом, можно создать Singleton: final Set<T> SOME_SET = Collections.singleton(t); - person Valentin Grégoire; 09.07.2019

Есть несколько способов:

Инициализация двойной скобки

Это метод, который создает анонимный внутренний класс, который имеет инициализатор экземпляра, который добавляет к себе Strings при создании экземпляра:

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

Имейте в виду, что это фактически создаст новый подкласс HashSet каждый раз, когда он используется, даже если не нужно явно писать новый подкласс.

Служебный метод

Написание метода, возвращающего Set, который инициализировать желаемыми элементами не так уж сложно:

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

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

Приведенный выше код позволяет использовать только String, но не должно быть слишком сложно разрешить использование любого типа с помощью универсальных шаблонов.

Используйте библиотеку

Многие библиотеки имеют удобный метод инициализации объектов коллекций.

Например, в Google Collections есть _ 7_, который заполнит HashSet элементы определенного типа.

person coobird    schedule 11.01.2010
comment
В GoogleCollections также есть ImmutableSet.of(T...). В большинстве случаев при этом вам не нужно снова менять набор позже, и в этом случае использование неизменяемого набора имеет много преимуществ. - person Kevin Bourrillion; 11.01.2010
comment
Чтобы уточнить, будет создавать новый подкласс HashSet каждый раз, когда он используется, означает каждый раз, когда он кодируется. Каждое выполнение не создает новый подкласс. - person Bohemian♦; 13.01.2014
comment
Инициирование двойной скобки может быть опасным, если вы не знаете, что делаете, поищите последствия, прежде чем использовать - person Amalgovinus; 07.03.2019

Один из наиболее удобных способов - использовать общий Collections.addAll (), который принимает коллекцию и переменные:

Set<String> h = new HashSet<String>();
Collections.addAll(h, "a", "b");
person Michael Berdyshev    schedule 22.11.2016
comment
Спасибо! Рад, что я прочитал дальше, я думаю, это должно быть ранжировано выше. Это отлично подходит для Java 8 и ниже; никакого копирования массивов и неуклюжих потоков. Спасибо! - person redeveloper; 08.06.2018
comment
Это отличный ответ, потому что в отличие от многих других ответов, насколько я могу судить, он дает изменяемый набор;) - person cellepo; 30.01.2019
comment
Я думаю, что это доступно как минимум в Java 5+. - person cellepo; 30.01.2019

Если у вас есть только одно значение и вы хотите получить неизменяемый набор, этого будет достаточно:

Set<String> immutableSet = Collections.singleton("a");
person Lu55    schedule 05.10.2015
comment
Это было очень полезно, потому что наш инструмент анализа кода жалуется, если вы используете метод new HashSet<>(Arrays.asList( _values_ )) только с одним значением. - person yokeho; 29.08.2017
comment
Остерегайтесь, результирующий Set ‹String› неизменен. - person ; 20.09.2017

С Java 9 вы можете делать следующее:

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

и вы получите неизменный Set, содержащий элементы. Подробнее см. документацию по интерфейсу Set Oracle. .

person Mathias Bader    schedule 05.07.2017

Вы можете сделать это в Java 6:

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

Но почему? Я не считаю его более читабельным, чем явное добавление элементов.

person Jason Nichols    schedule 11.01.2010
comment
Я создаю это в декларации. Это последнее свойство. Спасибо - person Serg; 11.01.2010
comment
Ах. Вероятно, стоит отметить, что вы все еще можете добавлять элементы в окончательный набор. Просто объявите набор final, но добавьте элементы в конструктор (или где-нибудь еще), как обычно. В окончательной Коллекции все еще могут быть добавлены или удалены элементы. Только ссылка на саму Коллекцию, если она окончательная. - person Jason Nichols; 11.01.2010
comment
+1 к тому, что сказал Джейсон Николс. Не забудьте использовать Collections.unmodifiableSet (...). - person Eli Acherkan; 11.01.2010
comment
я имею в виду, было бы более читабельно, если бы это было new HashSet<String>("a", "b", "c"); - person sam boosalis; 18.04.2014
comment
Обратите внимание, что Arrays # asList возвращает структурно неизменяемый список - person cellepo; 30.01.2019

Я считаю, что наиболее удобочитаемым является использование google Guava:

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

Это изменчиво.

person LanceP    schedule 29.10.2014
comment
Теперь это Sets.newHashSet(). - person membersound; 01.03.2016
comment
@membersound из какого пакета мне следует импортировать, чтобы использовать Sets.newHashSet ()? - person Abdennacer Lachiheb; 05.10.2016
comment
@AbdennacerLachiheb com.google.common.collect.Sets - person sachintha hewawasam; 20.04.2020
comment
@membersound Я проверял старые документы до guava 2.0, нет newSet (), я думаю, LanceP ссылался на какой-то другой фреймворк, например gs-collections. Итак, изменил ответ, как вы сказали. Спасибо - person Satish Patro; 30.04.2021

Обобщение вспомогательной функции ответа от coobird для создания новых HashSets:

public static <T> Set<T> newHashSet(T... objs) {
    Set<T> set = new HashSet<T>();
    for (T o : objs) {
        set.add(o);
    }
    return set;
}
person Mark Elliot    schedule 29.08.2010
comment
init с размером, пожалуйста + добавьте нулевую проверку - person Spektakulatius; 04.03.2020

Если содержащийся тип Set является перечислением, тогда существует встроенный фабричный метод java (начиная с версии 1.5):

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

В Eclipse Collections есть несколько различных способов инициализировать Set, содержащий символы 'a' и ' b 'в одном заявлении. В Eclipse Collections есть контейнеры как для объектных, так и для примитивных типов, поэтому я проиллюстрировал, как можно использовать Set<String> или _ 3_ в дополнение к изменяемым, неизменяемым, синхронизированным и неизменяемым версиям обоих.

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.

person Donald Raab    schedule 29.02.2016

(уродливо) Инициализация двойной скобки без побочных эффектов:

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

Но в некоторых случаях, если мы упомянули, что это хороший запах для неизменяемости финальных коллекций, это может быть действительно полезно:

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

Немного запутано, но работает с Java 5:

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

Используйте вспомогательный метод, чтобы сделать его читабельным:

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

public Set<String> asSet(String... values) {
    return new HashSet<String>(java.util.Arrays.asList(values));
}
person Aaron Digulla    schedule 11.01.2010
comment
(Вам не нужен new String[] {) - person Kevin Bourrillion; 11.01.2010

Небольшое примечание, независимо от того, какой из упомянутых здесь подходов вы получите, если это значение по умолчанию, которое обычно остается неизменным (например, настройка по умолчанию в создаваемой вами библиотеке), рекомендуется следовать этому шаблону. :

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

Преимущество зависит от количества созданных вами экземпляров этого класса и от того, насколько вероятно, что значения по умолчанию будут изменены.

Если вы решите следовать этому шаблону, вы также можете выбрать наиболее читаемый метод инициализации набора. Поскольку микроразличия в эффективности между различными методами, вероятно, не будут иметь большого значения, поскольку вы инициализируете набор только один раз.

person Amr Mostafa    schedule 11.11.2015

Используя Java 8, мы можем создать HashSet как:

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

И если нам нужен неизменяемый набор, мы можем создать служебный метод как:

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

Этот метод можно использовать как:

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

С выпуском java9 и удобные фабричные методы, это возможно более чистым способом:

Set set = Set.of("a", "b", "c");
person Naman    schedule 27.09.2017
comment
@cellepo Что ж, история редактирования этого ответа предполагает, что он был отредактирован позже. Хотя мы согласны, что содержание такое же, и этот ответ охватывает гораздо больше способов решения вопроса. - person Naman; 30.01.2019

Можно использовать статический блок для инициализации:

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);
}
person Ups    schedule 17.05.2017

Это элегантное решение:

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);
            }
        };
}
person ToxiCore    schedule 12.05.2015
comment
serialVersionUID - невероятно плохая идея во всех случаях - person Software Engineer; 31.03.2017

Здесь может пригодиться паттерн Строитель. Сегодня у меня была такая же проблема. где мне нужны операции изменения Set, чтобы вернуть мне ссылку на объект Set, поэтому я могу передать его конструктору суперкласса, чтобы они тоже могли продолжать добавлять в тот же набор, в свою очередь создавая новый StringSetBuilder из набора, который дочерний класс только что построен. Написанный мною класс строителя выглядит следующим образом (в моем случае это статический внутренний класс внешнего класса, но он также может быть его собственным независимым классом):

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

Обратите внимание на методы addAll() и add(), которые Set возвращают аналоги Set.add() и Set.addAll(). Наконец, обратите внимание на метод build(), который возвращает ссылку на Set, который инкапсулирует построитель. Ниже показано, как использовать этот конструктор Set:

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());
    }
}
person Jose Quijada    schedule 03.11.2017

Комбинируя ответ Михаила Бердышева с Generics и используя конструктор с initialCapacity, сравнивая с вариантом Arrays.asList:

  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
  }
  • Это хорошо, если вы передадите несколько значений для init, для чего-то большого используйте другие методы
  • Если вы случайно смешали типы с buildSetModif, в результате T будет ? extends Object, что, вероятно, не то, что вы хотите, этого не может произойти с вариантом buildSetModifTypeSafe, а это означает, что buildSetModifTypeSafe(1, 2, "a"); не будет компилироваться
person Christophe Roussy    schedule 27.05.2019

Вы также можете использовать vavr:

import io.vavr.collection.HashSet;

HashSet.of("a", "b").toJavaSet();
person Def_Os    schedule 20.10.2020