Pergunta

Eu preciso criar um Set com valores iniciais.

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

Existe uma maneira de fazer isso em uma linha de código?Por exemplo, é útil para um campo estático final.

Foi útil?

Solução

Há uma abreviação que eu uso que não é muito eficiente, mas se encaixa em uma única linha:

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

Novamente, isso não é eficiente em termos de tempo, pois você está construindo uma matriz, convertendo em uma lista e usando essa lista para criar um conjunto.

Ao inicializar os conjuntos finais estáticos, geralmente escrevo assim:

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

Um pouco menos feio e a eficiência não importa para a inicialização estática.

Outras dicas

Os literais da coleção estavam programados para o Java 7, mas ainda não chegaram. Portanto, nada de automático ainda.

Você pode usar a goiaba's Sets:

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

Ou você pode usar a seguinte sintaxe, que criará uma aula anônima, mas é hacky:

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

Em Java 8, eu usaria:

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

Isso te dá um mutável Set pré-inicializado com "A" e "B". Observe que, enquanto estiver no JDK 8, isso retorna um HashSet, a especificação não garante, e isso pode mudar no futuro. Se você quer especificamente um HashSet, faça isso em vez disso:

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

Usando Java 10 (conjuntos não modificáveis)

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

Aqui o colecionador realmente retornaria o conjunto não modificável introduzido em Java 9, como evidente na declaração set -> (Set<T>)Set.of(set.toArray()) no código -fonte.

Um ponto para notar é esse o método Collections.unmodifiableSet() Retorna uma visão não modificável do conjunto especificado (conforme documentação). Um visão não modificável A coleção é uma coleção que não é modificável e também é uma vista em uma coleção de apoio. Observe que as mudanças na coleção de apoio poderia Ainda são possíveis e, se ocorrerem, são visíveis através da visão não modificável. Mas o método Collectors.toUnmodifiableSet() retorna verdadeiramente imutável instalado Java 10.


Usando Java 9 (conjuntos não modificáveis)

O seguinte é o mais compacto maneira de inicializar um conjunto:

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

Temos 12 versões sobrecarregadas deste Fábrica de conveniência método:

static <E> Set<E> of()

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

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

// ....e assim por diante

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

Então uma pergunta natural é Por que precisamos de versões sobrecarregadas quando temos var-args? A resposta é: todo método var-arg cria uma matriz internamente e ter as versões sobrecarregadas evitaria a criação desnecessária de objeto e também nos salvará da sobrecarga da coleta de lixo.


Usando Java 8 (conjuntos modificáveis)

Usando Fluxo em 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));

Usando Java 8 (conjuntos não modificáveis)

Usando coleções.unmodifiableSet ()

Podemos usar Collections.unmodifiableSet() Como:

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

Mas parece um pouco estranho e podemos escrever nosso próprio colecionador como o seguinte:

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

E então use -o como:

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


Usando colecionadores.CollectingAndHen ()

Outra abordagem é usar o método Collectors.collectingAndThen() O que nos permite realizar transformações adicionais de acabamento:

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

Se apenas nos preocuparmos com Set Então também podemos usar Collectors.toSet() no lugar de Collectors.toCollection(HashSet::new).

Existem algumas maneiras:

Inicialização de suporte duplo

Esta é uma técnica que cria uma classe interna anônima que possui um inicializador de instância que adiciona Strings para si mesmo quando uma instância é criada:

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

Lembre -se de que isso realmente criará uma nova subclasse de HashSet Cada vez que é usado, mesmo que não seja necessário escrever explicitamente uma nova subclasse.

Um método de utilidade

Escrever um método que retorne um Set que é inicializado com os elementos desejados não é muito difícil de escrever:

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

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

O código acima permite apenas o uso de um String, mas não deve ser muito difícil permitir o uso de qualquer tipo usando genéricos.

Use uma biblioteca

Muitas bibliotecas têm um método de conveniência para inicializar objetos de coleções.

Por exemplo, Coleções do Google tem um Sets.newHashSet(T...) método que preencherá um HashSet com elementos de um tipo específico.

Se você tem apenas um valor e deseja obter um imutável Definir isso seria suficiente:

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

Uma das maneiras mais convenientes é o uso de genéricos Coleções.addall () Método, que leva uma coleção e varargs:

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

Você pode fazer isso em Java 6:

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

Mas por que? Não acho que seja mais legível do que adicionar explicitamente elementos.

Eu sinto que o mais legível é simplesmente usar o Google Guava:

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

Com o Java 9, você pode fazer o seguinte:

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

E você receberá um conjunto imutável contendo os elementos. Para detalhes, veja o Oracle Documentação do conjunto de interface.

Uma generalização de Resposta de Coobird Função de utilidade para criar novos HashSets:

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

Se o tipo contido do conjunto for uma enumeração, existe um método de fábrica construído por Java (desde 1.5):

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

Com Coleções Eclipse existem algumas maneiras diferentes de inicializar um Set contendo os caracteres 'a' e 'b' em uma instrução.Eclipse Collections possui contêineres para tipos de objetos e primitivos, então ilustrei como você poderia usar um Set<String> ou CharSet além de versões mutáveis, imutáveis, sincronizadas e não modificáveis ​​de ambos.

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

Coleções Eclipse são compatíveis com Java 5 - 8.

Observação:Eu sou um committer das Coleções Eclipse.

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

ou

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

(feia) inicialização dupla sem efeitos colaterais:

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

Mas, em alguns casos, se mencionamos que esse é um bom cheiro para tornar as coleções finais inútil, pode ser realmente útil:

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

Um pouco complicado, mas funciona do Java 5:

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

Use um método auxiliar para torná -lo legível:

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

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

Apenas uma pequena nota, independentemente das abordagens finas mencionadas aqui, você acaba, se esse é um padrão que geralmente não é modificado (como uma configuração padrão em uma biblioteca que você está criando), é uma boa ideia seguir esse padrão :

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

O benefício depende do número de instâncias que você cria dessa classe e com que probabilidade de que os padrões sejam alterados.

Se você decidir seguir esse padrão, também poderá escolher o método de inicialização do conjunto que é mais legível. Como as micro diferenças na eficiência entre os diferentes métodos provavelmente não importam muito, pois você inicializará o conjunto apenas uma vez.

Usando Java 8 nós podemos criar HashSet Como:

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

E se queremos um conjunto não modificável, podemos criar um método de utilitário como:

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

Este método pode ser usado como:

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

Com o lançamento de e a Métodos de fábrica de conveniência Isso é possível de uma maneira mais limpa:

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

Pode usar o bloco estático para inicialização:

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

Esta é uma solução elegante:

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

O padrão do construtor pode ser útil aqui. Hoje eu tive o mesmo problema. onde eu precisava definir operações de mutação para me devolver uma referência do objeto Set, para que eu possa passar para o construtor de super classe, para que eles também possam continuar adicionando ao mesmo conjunto por sua vez construindo um novo Stringsetbuilder a partir do conjunto que a classe infantil Apenas construído. A classe do construtor que escrevi se parece com isso (no meu caso, é uma classe interna estática de uma classe externa, mas também pode ser sua própria classe independente):

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

Observe o addAll() e add() Métodos, que estão devolvendo contrapartes de Set.add() e Set.addAll(). Finalmente observe o build() Método, que retorna uma referência ao conjunto que o construtor encapsula. Abaixo ilustra, em seguida, como usar este conjunto de conjuntos:

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

Combinando a resposta por Michael Berdyshev com genéricos e usando construtor com capacidade inicial, comparando com Arrays.asList variante:

  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
  }
  • Isso é bom se você passar alguns valores para init, para qualquer coisa grande uso outros métodos
  • Se você misturar tipos acidentalmente com buildSetModif o T resultante será ? extends Object, que provavelmente não é o que você quer, isso não pode acontecer com o buildSetModifTypeSafe variante, o que significa que buildSetModifTypeSafe(1, 2, "a"); não vai compilar
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top