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

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

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

есть ли способ сделать это в одну строку кода?

22 ответов


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

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

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


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

вы можете использовать гуава это Sets:

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

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

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

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

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

двойной дубль инициализации

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

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

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

A метод полезности

написание метода, который возвращает 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 Коллекции есть Sets.newHashSet(T...) метод, который будет заполнять HashSet С ЭЛЕМЕНТАМИ определенного типа.


Использование Java 10 (Немодифицируемые Наборы)

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

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

Использование Java 9 (Немодифицируемые Наборы)

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

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

используя поток в 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 (Немодифицируемые Наборы)

использование Коллекции.unmodifiableSet - мы можем использовать Collections.unmodifiableSet as:

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

С Помощью Коллекторов.collectingAndThen - другой подход заключается в использовании метода Collectors.collectingAndThen что позволяет нам выполнять дополнительные завершающие преобразования:

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

если мы только заботимся о Set затем мы также можем использовать Collectors.toSet() на месте Collectors.toCollection(HashSet::new).


одно замечание это и есть метод Collections::unmodifiableSet возвращает неизменяемое представление указанного набора в соответствии с doc. Немодифицируемая коллекция представлений-это немодифицируемая коллекция, а также представление резервной коллекции. Обратите внимание, что изменения в резервной коллекции все еще могут быть возможны, и если они происходят, они видны через немодифицируемое представление. Но метод Collectors.unmodifiableList возвращает по-настоящему неизменяемый набор в Java 10.


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

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

но почему? Я не нахожу его более читаемым, чем явное добавление элементов.


Если у вас есть только одно начальное значение в наборе, этого будет достаточно:

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

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

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

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

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

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


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

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

обобщение 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;
}

если содержащийся тип набора является перечислением, то существует Java built factory method (начиная с 1.5):

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

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

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 совместимы с Java 5-8.

Примечание: я коммиттер для коллекций Eclipse.


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

или

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

немного запутанный, но работает с 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));
}

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

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

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

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

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

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


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

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

используя Java 8 мы можем создать HashSet as:

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

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

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

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

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

шаблон Builder может быть полезен здесь. Сегодня у меня была та же проблема. где мне нужно было установить операции мутации, чтобы вернуть мне ссылку на объект 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.add() и Set.addAll(). Наконец, обратите внимание на build() метод, который возвращает ссылку на набор строитель инкапсулирует. Ниже показано, как использовать этот конструктор наборов:

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