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

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

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

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

Ответ 1

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

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

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

Ответ 2

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

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

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

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

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

Ответ 3

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

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

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

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

Ответ 4

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

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

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

Следует отметить, что метод Collections.unmodifiableSet() возвращает неизменяемое представление указанного набора (согласно документации). Неизменяемая коллекция представлений - это коллекция, которая не может быть изменена, а также является представлением резервной коллекции. Обратите внимание, что изменения в резервной коллекции все еще возможны, и если они происходят, они видны через неизменяемое представление. Но метод Collectors.toUnmodifiableSet() возвращает действительно неизменный набор в 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());


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

Ответ 5

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

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

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

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 имеет Sets.newHashSet(T...), который заполнит элемент HashSet элементами определенного типа.

Ответ 6

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

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

Ответ 7

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

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

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

Ответ 8

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

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

Ответ 9

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

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

Ответ 10

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

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

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

Ответ 11

Обобщение функции утилиты coobird answer для создания нового 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;
}

Ответ 12

Если содержащийся тип Set является перечислением, тогда существует Java-метод factory (начиная с версии 1.5):

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

Ответ 13

С Eclipse Collections существует несколько различных способов инициализации Set, содержащего символы 'a' и 'b' в одном выражении, Eclipse Collections имеет контейнеры как для объектных, так и для примитивных типов, поэтому я проиллюстрировал, как вы можете использовать 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 Collections совместим с Java 5-8.

Примечание. Я являюсь коммиттером для коллекций Eclipse.

Ответ 14

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

или

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

Ответ 15

Немного свернуто, но работает с 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));
}

Ответ 16

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

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

Ответ 17

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

// Initialize default values with the method you prefer, even in a static block
// It 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;

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

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

Ответ 18

Используя 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));

Ответ 20

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

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

Ответ 21

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

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

Ответ 22

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

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(), который возвращает ссылку на 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());
    }
}

Ответ 23

Комбинируя ответ Майкл Бердышев с Дженерики и с помощью конструктора с 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 типы с buildSetModif ли T ? extends Object ? extends Object, что, вероятно, не то, что вам нужно, это не может произойти с вариантом buildSetModifTypeSafe, что означает, что buildSetModifTypeSafe(1, 2, "a"); не будет компилироваться