Como classificar um HashSet?

106

Para listas, usamos o Collections.sort(List)método. E se quisermos classificar um HashSet?

Diana
fonte
20
A HashSeté uma coleção não ordenada.
Alexis C.
2
Você não pode, já Setque a não tem métodos de acesso aleatório (ou seja, .get()um elemento em um determinado índice), que é basicamente necessário para algoritmos de classificação;)
fge
3
Você poderia convertê-lo em uma lista primeiro e, em seguida, classificar se precisar classificar
demongolem
Você não pode, pois a HashSetnão tem uma ordem definida. Sua pergunta contém uma contradição de termos.
Marquês de Lorne
1
use um TreeSet e se você não puder controlar a fonte, veja a conversão e um uso aqui stackoverflow.com/a/52987487/5153955
Tenflex

Respostas:

114

Um HashSet não garante nenhuma ordem de seus elementos. Se você precisar dessa garantia, considere usar um TreeSet para manter seus elementos.

No entanto, se você só precisa que seus elementos sejam classificados para esta ocorrência, crie temporariamente uma Lista e classifique-a:

Set<?> yourHashSet = new HashSet<>();

...

List<?> sortedList = new ArrayList<>(yourHashSet);
Collections.sort(sortedList);
isak gilbert
fonte
1
Além disso, se você estiver usando uma coleção de Strings, entãoList<String> sortedList = new ArrayList<String>(yourHashSet);
wisbucky
65

Adicione todos os seus objetos ao TreeSet, você obterá um Conjunto ordenado. Abaixo está um exemplo bruto.

HashSet myHashSet = new HashSet();
myHashSet.add(1);
myHashSet.add(23);
myHashSet.add(45);
myHashSet.add(12);

TreeSet myTreeSet = new TreeSet();
myTreeSet.addAll(myHashSet);
System.out.println(myTreeSet); // Prints [1, 12, 23, 45]
Abdullah Khan
fonte
2
Ao usar, TreeSet myTreeSet = new TreeSet(myHashSet);você pode evitar adicionar todos os elementos ao Treeset novamente.
Mounika
17

Você pode usar um TreeSet .

carvalho
fonte
1
Apenas colocar os elementos não dará a flexibilidade de classificação em qualquer ordem com qualquer elemento dentro dela. A solução acima sim.
Jess
15

A maneira do Java 8 de classificar seria:

fooHashSet.stream()
  .sorted(Comparator.comparing(Foo::getSize)) //comparator - how you want to sort it
  .collect(Collectors.toList()); //collector - what you want to collect it to

* Foo::getSizeé um exemplo de como classificar o HashSet de YourItem naturalmente por tamanho.

* Collectors.toList()vai coletar o resultado da classificação em uma lista que você precisará capturar comList<Foo> sortedListOfFoo =

LazerBanana
fonte
Você pode, por favor, adicionar a lógica para classificá-la em uma ordem específica?
Jess
@Jess eu não sei qual é o pedido específico para você Jess, você pode classificá-lo como quiser usando o comparador.
LazerBanana
Eu quis dizer, como definir o Ascendente ou Descendente
Jess
14

Use java.util.TreeSetcomo o objeto real. Quando você itera nessa coleção, os valores voltam em uma ordem bem definida.

Se você usar java.util.HashSet, a ordem depende de uma função hash interna que quase certamente não é lexicográfica (com base no conteúdo).

P45 iminente
fonte
Por que você está presumindo que eles armazenam Stringvalores?
Sotirios Delimanolis
Não estou, talvez meu uso de lexográfico seja impreciso ;-)
P45 Iminente
3
É muito errado. Ele não armazena chaves em ordem lexográfica (sp?). Ele usa sua ordem natural (que depende da Comparableinterface que as chaves implementam) ou usa a fornecida Comparator.
Sotirios Delimanolis
Eu editei. Melhor você acha, ou devo deletar a resposta?
P45 iminente
1
Caso você mova um HashSetpara TreeSet, sua classe deve implementar uma Comparableinterface ou fornecer um personalizado Comparator. Caso contrário, como você não pode classificar um HashSet, apenas converta-o em a Liste classifique-o.
Luiggi Mendoza
5

Você pode usar coletores Java 8 e TreeSet

list.stream().collect(Collectors.toCollection(TreeSet::new))

Ninja
fonte
new TreeSet<>(hashSet)é mais conciso e provavelmente mais eficiente.
devconsole
4

Você pode usar TreeSet conforme mencionado em outras respostas.

Aqui está um pouco mais de elaboração sobre como usá-lo:

TreeSet<String> ts = new TreeSet<String>();
ts.add("b1");
ts.add("b3");
ts.add("b2");
ts.add("a1");
ts.add("a2");
System.out.println(ts);
for (String s: ts)
    System.out.println(s);

Resultado:

[a1, a2, a3, a4, a5]
a1
a2
b1
b2
b3
Alisa
fonte
4

Os elementos no HashSet não podem ser classificados. Sempre que você coloca elementos no HashSet, pode bagunçar a ordem de todo o conjunto. É deliberadamente projetado assim para desempenho. Quando você não se importa com o pedido, o HashSet será o conjunto mais eficiente para inserção e pesquisa rápidas.

TreeSet classificará todos os elementos automaticamente sempre que você inserir um elemento.

Talvez o que você esteja tentando fazer seja classificar apenas uma vez. Nesse caso, TreeSet não é a melhor opção porque precisa determinar a colocação de elementos recém-adicionados o tempo todo.

A solução mais eficiente é usar ArrayList. Crie uma nova lista e adicione todos os elementos e, em seguida, classifique-a uma vez. Se você quiser reter apenas elementos únicos (remova todas as duplicatas como o conjunto faz e, em seguida, coloque a lista em um LinkedHashSet, ele manterá a ordem que você já classificou)

List<Integer> list = new ArrayList<>();
list.add(6);
list.add(4);
list.add(4);
list.add(5);
Collections.sort(list);
Set<Integer> unique = new LinkedHashSet<>(list); // 4 5 6
// The above line is not copying the objects! It only copies references.

Agora, você obteve um conjunto classificado se quiser em uma forma de lista e então converta-o em lista.

off99555
fonte
3

Com base na resposta dada por @LazerBanana, irei colocar meu próprio exemplo de um Conjunto classificado pelo Id do Objeto:

Set<Clazz> yourSet = [...];

yourSet.stream().sorted(new Comparator<Clazz>() {
    @Override
    public int compare(Clazz o1, Clazz o2) {
        return o1.getId().compareTo(o2.getId());
    }
}).collect(Collectors.toList()); // Returns the sorted List (using toSet() wont work)
Leandro S
fonte
3

Apenas no caso de você não querer usar um, TreeSetvocê pode tentar isso.

set = set.stream().sorted().collect(Collectors.toCollection(LinkedHashSet::new));
Ankit Sharma
fonte
2

Na minha humilde opinião, a resposta de LazerBanana deve ser a resposta mais bem avaliada e aceita porque todas as outras respostas que apontam para java.util.TreeSet(ou primeiro converta para lista e depois chame Collections.sort(...)a lista convertida) não se preocuparam em perguntar a OP como que tipo de objetos você HashSettem. se esses elementos têm uma ordem natural predefinida ou não e essa não é uma questão opcional, mas uma questão obrigatória.

Você simplesmente não pode entrar e começar a colocar seus HashSetelementos em um TreeSettipo de elemento if ainda não implementa a Comparableinterface ou se você não estiver passando explicitamente Comparatorpara o TreeSetconstrutor.

Do TreeSetJavaDoc,

Constrói um novo conjunto de árvore vazio, classificado de acordo com a ordem natural de seus elementos. Todos os elementos inseridos no conjunto devem implementar a interface Comparable. Além disso, todos esses elementos devem ser mutuamente comparáveis: e1.compareTo (e2) não deve lançar uma ClassCastException para quaisquer elementos e1 e e2 no conjunto. Se o usuário tentar adicionar um elemento ao conjunto que viole essa restrição (por exemplo, o usuário tenta adicionar um elemento de string a um conjunto cujos elementos são inteiros), a chamada add lançará uma ClassCastException.

É por isso que apenas todas as respostas baseadas em fluxo Java8 - onde você define seu comparador no local - só fazem sentido porque implementar comparável em POJO se torna opcional. O programador define o comparador como e quando necessário. Tentar coletar TreeSetsem fazer essa pergunta fundamental também é incorreto (resposta de Ninja). Supondo que os tipos de objeto sejam Stringou Integertambém estejam incorretos.

Dito isso, outras preocupações como,

  1. Desempenho de classificação
  2. Memory Foot Print (mantendo o conjunto original e criando novos conjuntos classificados cada vez que a classificação é feita ou deseja classificar o conjunto no local etc etc)

devem ser os outros pontos relevantes também. Apenas apontar para a API não deve ser apenas uma intenção.

Como o conjunto original já contém apenas elementos únicos e essa restrição também é mantida pelo conjunto classificado, o conjunto original precisa ser apagado da memória, uma vez que os dados são duplicados.

Sabir Khan
fonte
1
1. Add all set element in list -> al.addAll(s);
2. Sort all the elements in list using -> Collections.sort(al);


 public class SortSetProblem {
 public static void main(String[] args) {
    ArrayList<String> al = new ArrayList();
    Set<String> s = new HashSet<>();
    s.add("ved");
    s.add("prakash");
    s.add("sharma");
    s.add("apple");
    s.add("ved");
    s.add("banana");
    System.out.println("Before Sorting");
    for (String s1 : s) {
        System.out.print("  " + s1);
    }

    System.out.println("After Sorting");
    al.addAll(s);
    Collections.sort(al);
    for (String set : al) {
        System.out.print(" " + set);
    }
  }
 }

input - ved prakash sharma apple ved banana

Resultado - apple banana prakash sharma ved

Ved Prakash
fonte
1

Se você deseja que o final Collectiontenha a forma de Sete se deseja definir o seu próprio natural orderem vez de TreeSetentão -

1. Converta o HashSetem List
2. Classifique o personalizado Listusando Comparator
3. Converta de volta o Listem LinkedHashSetpara manter a ordem
4. Exiba o LinkedHashSet

Programa de amostra -

package demo31;

import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

public class App26 {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        addElements(set);
        List<String> list = new LinkedList<>();
        list = convertToList(set);
        Collections.sort(list, new Comparator<String>() {
            @Override
            public int compare(String s1, String s2) {
                int flag = s2.length() - s1.length();
                if(flag != 0) {
                    return flag;
                } else {
                    return -s1.compareTo(s2);
                }
            }
        });
        Set<String> set2 = new LinkedHashSet<>();
        set2 = convertToSet(list);
        displayElements(set2);
    }
    public static void addElements(Set<String> set) {
        set.add("Hippopotamus");
        set.add("Rhinocerous");
        set.add("Zebra");
        set.add("Tiger");
        set.add("Giraffe");
        set.add("Cheetah");
        set.add("Wolf");
        set.add("Fox");
        set.add("Dog");
        set.add("Cat");
    }
    public static List<String> convertToList(Set<String> set) {
        List<String> list = new LinkedList<>();
        for(String element: set) {
            list.add(element);
        }
        return list;
    }
    public static Set<String> convertToSet(List<String> list) {
        Set<String> set = new LinkedHashSet<>();
        for(String element: list) {
            set.add(element);
        }
        return set;
    }
    public static void displayElements(Set<String> set) {
        System.out.println(set);
    }
}

Resultado -

[Hippopotamus, Rhinocerous, Giraffe, Cheetah, Zebra, Tiger, Wolf, Fox, Dog, Cat]

Aqui, a coleção foi classificada como -

Primeira - Ordem decrescente de Stringcomprimento
Segundo - Ordem decrescente da Stringhierarquia alfabética

Payel Senapati
fonte
0

você pode fazer isso das seguintes maneiras:

Método 1:

  1. Crie uma lista e armazene todos os valores de hashset nela
  2. classifique a lista usando Collections.sort ()
  3. Armazene a lista de volta em LinkedHashSet, pois preserva o pedido de inserção

Método 2:

  • Crie um treeSet e armazene todos os valores nele.

O método 2 é mais preferível porque o outro método consome muito tempo para transferir dados entre o conjunto de hash e a lista.

Sujit
fonte
0

Não podemos decidir se os elementos de um HashSet seriam classificados automaticamente. Mas podemos classificá-los convertendo em TreeSet ou qualquer lista como ArrayList ou LinkedList etc.

// Create a TreeSet object of class E
TreeSet<E> ts = new TreeSet<E> ();

// Convert your HashSet into TreeSet
ts.addAll(yourHashSet);

System.out.println(ts.toString() + "\t Sorted Automatically");
alexvipul
fonte
0

Você pode usar a biblioteca de goiaba para o mesmo

Set<String> sortedSet = FluentIterable.from(myHashSet).toSortedSet(new Comparator<String>() {
    @Override
    public int compare(String s1, String s2) {
        // descending order of relevance
        //required code
    }
});
Ankita Bhowmik
fonte
0

Você pode envolvê-lo em um TreeSet como este:

Set mySet = new HashSet();
mySet.add(4);
mySet.add(5);
mySet.add(3);
mySet.add(1);
System.out.println("mySet items "+ mySet);   

TreeSet treeSet = new TreeSet(mySet);   
System.out.println("treeSet items "+ treeSet);   

saída:
itens mySet [1, 3, 4, 5]
itens treeSet [1, 3, 4, 5]

Set mySet = new HashSet();
mySet.add("five");
mySet.add("elf");
mySet.add("four");
mySet.add("six");
mySet.add("two");
System.out.println("mySet items "+ mySet);

TreeSet treeSet = new TreeSet(mySet);
System.out.println("treeSet items "+ treeSet);

saída:
itens mySet [seis, quatro, cinco, dois, elfo]
treeSet itens [elfo, cinco, quatro, seis, dois]

O requisito para este método é que os objetos do conjunto / lista sejam comparáveis ​​(implemente a interface Comparável)

velocidade
fonte
-4

Este comando simples funcionou para mim:

myHashSet.toList.sorted

Eu usei isso dentro de uma instrução de impressão, então se você realmente precisa persistir a ordem, você pode precisar usar TreeSets ou outras estruturas propostas neste thread.

Glenn Strycker
fonte
1
Não vejo onde HashSet ou Set tem um método toList.
Thomas Eizinger
1
Isso parece Scala para mim, o que infelizmente não resolve o problema em Java.
Roberto
método toList, como é possível?
Ved Prakash