Как инициализировать значения HashSet по построению? - PullRequest
651 голосов
/ 11 января 2010

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

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

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

Ответы [ 23 ]

845 голосов
/ 11 января 2010

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

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

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

332 голосов
/ 11 января 2010

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

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

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

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

Set<String> h = new HashSet<String>() {{
    add("a");
    add("b");
}};
176 голосов
/ 13 января 2014

В 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));
128 голосов
/ 24 мая 2016

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

85 голосов
/ 11 января 2010

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

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

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

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 элементами определенного типа.

34 голосов
/ 05 октября 2015

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

Set<String> immutableSet = Collections.singleton("a");
27 голосов
/ 11 января 2010

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

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

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

26 голосов
/ 22 ноября 2016

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

Set<String> h = new HashSet<String>();
Collections.addAll(h, "a", "b");
24 голосов
/ 29 октября 2014

Мне кажется, что наиболее читаемым является просто использовать Google Guava:

Set<String> StringSet = Sets.newSet("a", "b", "c");
22 голосов
/ 05 июля 2017

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

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

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

.
...