Como manter uma lista única em Java?

104

Como criar uma lista de objetos únicos / distintos (sem duplicatas) em Java?

No momento estou usando HashMap<String, Integer>para fazer isso, pois a chave é sobrescrita e, portanto, no final podemos obter HashMap.getKeySet()qual seria única. Mas tenho certeza de que deveria haver uma maneira melhor de fazer isso, pois a parte do valor é desperdiçada aqui.

Basil Bourque
fonte

Respostas:

164

Você pode usar uma implementação Set :

Algumas informações do JAVADoc:

Uma coleção que não contém elementos duplicados . Mais formalmente, os conjuntos não contêm nenhum par de elementos e1 e e2 de modo que e1.equals (e2) e no máximo um elemento nulo. Como implícito em seu nome, essa interface modela a abstração matemática do conjunto.

Observação: muito cuidado deve ser tomado se objetos mutáveis ​​forem usados ​​como elementos de conjunto. O comportamento de um conjunto não é especificado se o valor de um objeto for alterado de maneira que afete as comparações de iguais enquanto o objeto for um elemento do conjunto. Um caso especial desta proibição é que não é permitido a um conjunto conter-se como um elemento.

Estas são as implementações:

  • HashSet

    Essa classe oferece desempenho de tempo constante para as operações básicas (adicionar, remover, conter e dimensionar), assumindo que a função hash dispersa os elementos adequadamente entre os baldes. A iteração desse conjunto requer um tempo proporcional à soma do tamanho da instância HashSet (o número de elementos) mais a "capacidade" da instância HashMap de apoio (o número de depósitos). Portanto, é muito importante não definir a capacidade inicial muito alta (ou o fator de carga muito baixo) se o desempenho da iteração for importante.

    Ao iterar a, HashSeta ordem dos elementos produzidos é indefinida.

  • LinkedHashSet

    Tabela de hash e implementação de lista vinculada da interface Set, com ordem de iteração previsível. Essa implementação difere do HashSet porque mantém uma lista duplamente vinculada em todas as suas entradas. Esta lista vinculada define a ordem das iterações, que é a ordem em que os elementos foram inseridos no conjunto (ordem de inserção). Observe que a ordem de inserção não é afetada se um elemento for inserido novamente no conjunto. (Um elemento e é reinserido em um conjunto s se s.add (e) for invocado quando s.contains (e) retornaria verdadeiro imediatamente antes da invocação.)

    Então, a saída do código acima ...

     Set<Integer> linkedHashSet = new LinkedHashSet<>();
     linkedHashSet.add(3);
     linkedHashSet.add(1);
     linkedHashSet.add(2);
    
     for (int i : linkedHashSet) {
         System.out.println(i);
     }

    ... será necessariamente

    3
    1
    2
  • TreeSet

    Esta implementação fornece custo de tempo log (n) garantido para as operações básicas (adicionar, remover e conter). Por padrão, os elementos retornados na iteração são classificados por sua " ordem natural ", portanto, o código acima ...

     Set<Integer> treeSet = new TreeSet<>();
     treeSet.add(3);
     treeSet.add(1);
     treeSet.add(2);
    
     for (int i : treeSet) {
         System.out.println(i);
     }

    ... produzirá este:

    1
    2
    3

    (Você também pode passar uma Comparatorinstância para um TreeSetconstrutor, fazendo com que ele classifique os elementos em uma ordem diferente.)

    Observe que a ordem mantida por um conjunto (seja ou não fornecido um comparador explícito) deve ser consistente com igual para implementar corretamente a interface do Conjunto. (Consulte Comparable ou Comparator para uma definição precisa de consistente com igual.) Isso ocorre porque a interface Set é definida em termos da operação igual, mas uma instância de TreeSet executa todas as comparações de elemento usando seu método compareTo (ou compare), então dois os elementos considerados iguais por este método são, do ponto de vista do conjunto, iguais. O comportamento de um conjunto é bem definido, mesmo se sua ordem for inconsistente com iguais; ele simplesmente não obedece ao contrato geral da interface Set.

Frank
fonte
Agora estou confuso, qual devo usar? Eu só preciso manter uma lista de strings exclusivas. Então, basicamente, mesmo quando uma string existente é adicionada, ela deve realmente ser adicionada.
1
A escolha é sua ... O HashSet é universal e rápido, o conjunto de árvores é solicitado, o LinkedHashset mantém a ordem de inserção ...
Frank
6
Isto não é uma LIST ... portanto, nem todos os métodos da interface LIST estão disponíveis.
marcolopes
2
Um conjunto não é uma lista, não posso procurar elementos por índice em um conjunto no tempo O (1) (acesso aleatório).
wilmol de
13

Eu quero esclarecer algumas coisas aqui para o pôster original que outros fizeram alusão, mas não declararam explicitamente. Quando você diz que deseja uma Lista Única, essa é a própria definição de um Conjunto Ordenado. Algumas outras diferenças importantes entre a interface Set e a interface List são que List permite que você especifique o índice de inserção. Portanto, a questão é: você realmente precisa da interface List (ou seja, para compatibilidade com uma biblioteca de terceiros, etc.), ou você pode reprojetar seu software para usar a interface Set? Você também deve considerar o que está fazendo com a interface. É importante encontrar elementos por seu índice? Quantos elementos você espera em seu set? Se você vai ter muitos elementos, o pedido é importante?

Se você realmente precisa de uma List que tem apenas uma restrição exclusiva, existe a classe Apache Common Utils org.apache.commons.collections.list.SetUniqueList que fornecerá a interface List e a restrição exclusiva. Veja bem, isso quebra a interface de lista. No entanto, você obterá um melhor desempenho com isso se precisar pesquisar a lista por índice. Se você pode lidar com a interface Set e tem um conjunto de dados menor, LinkedHashSet pode ser uma boa opção. Depende apenas do design e da intenção do seu software.

Novamente, existem certas vantagens e desvantagens em cada coleção. Algumas inserções rápidas, mas leituras lentas, algumas têm leituras rápidas, mas inserções lentas, etc. Faz sentido gastar uma boa quantidade de tempo com a documentação das coleções para aprender completamente sobre os detalhes mais sutis de cada classe e interface.

Paul Connolly
fonte
3
Isso não fornece uma resposta para a pergunta. Para criticar ou solicitar esclarecimentos de um autor, deixe um comentário abaixo de sua postagem - você sempre pode comentar suas próprias postagens e, assim que tiver reputação suficiente , poderá comentar em qualquer postagem .
Zach Saucier
1
Ele fornece uma resposta, na verdade. Se ele quiser apenas uma lista que atue como um Set, use org.apache.commons.collections.list.SetUniqueList, mas como um programador, ele / nós devemos ser mais cuidadosos do que isso e pensar mais sobre o problema. Se isso tornar minha resposta melhor, "Como criar uma lista exclusiva em Java?" List uniqueList = new SetUniqueList () ;, é assim ...
Paul Connolly
3
E Zach, não estou tentando ser um idiota, mas você ao menos leu minha resposta antes de seu comentário? Ou você simplesmente não entende isso? Se você não entender, tudo bem - me avise e irei expandir o assunto. Não acho que deva escrever um tratado sobre estruturas de dados para dar uma resposta amigável à pergunta de alguém. Também não me importo de criar uma maneira mansa de construir minha reputação de comentário quando sei a resposta e ninguém mais a forneceu.
Paul Connolly
1
E por falar nisso, eu não estava criticando ou pedindo esclarecimentos ao autor, eu estava apenas dizendo que ele pode A) usar rapidamente a aula que dei a ele, ou B) ter tempo para realmente entender as diferenças entre essas classes e se relacionar eles às suas necessidades. B obviamente leva mais tempo, mas resultará em um código melhor a longo prazo.
Paul Connolly
8

Use new HashSet<String> um exemplo:

import java.util.HashSet;
import java.util.Set;

public class MainClass {
  public static void main(String args[]) {
    String[] name1 = { "Amy", "Jose", "Jeremy", "Alice", "Patrick" };

    String[] name2 = { "Alan", "Amy", "Jeremy", "Helen", "Alexi" };

    String[] name3 = { "Adel", "Aaron", "Amy", "James", "Alice" };

    Set<String> letter = new HashSet<String>();

    for (int i = 0; i < name1.length; i++)
      letter.add(name1[i]);

    for (int j = 0; j < name2.length; j++)
      letter.add(name2[j]);

    for (int k = 0; k < name3.length; k++)
      letter.add(name3[k]);

    System.out.println(letter.size() + " letters must be sent to: " + letter);

  }
}
tim_a
fonte
2
Apenas adicionando o programa acima -> 11 cartas devem ser enviadas para: [Aaron, Alice, James, Adel, Jose, Jeremy, Amy, Alan, Patrick, Helen, Alexi]
Ammad
4

Você pode apenas usar um HashSet<String>para manter uma coleção de objetos exclusivos. Se os Integervalores em seu mapa são importantes, então você pode usar o containsKeymétodo de mapas para testar se sua chave já está no mapa.

Ted Hopp
fonte
3

HashSet<String>(ou) qualquer Setimplementação pode fazer o trabalho para você. Setnão permite duplicatas.

Aqui está o javadoc para HashSet.

kosa
fonte
2

Não sei o quão eficiente isso é, porém funcionou para mim em um contexto simples.

List<int> uniqueNumbers = new ArrayList<>();

   public void AddNumberToList(int num)
    {
        if(!uniqueNumbers .contains(num)) {
            uniqueNumbers .add(num);
        }
    }
Zapnologica
fonte
1

Você pode querer usar uma das classes de implementação da java.util.Set<E>Interface, por exemplo, java.util.HashSet<String> classe de coleção.

Uma coleção que não contém elementos duplicados. Mais formalmente, os conjuntos não contêm nenhum par de elementos e1 e e2 de modo que e1.equals (e2) e no máximo um elemento nulo. Como implícito em seu nome, essa interface modela a abstração matemática do conjunto.

Yogendra Singh
fonte