Coleção classificada em Java

161

Sou iniciante em Java. Por favor, sugira quais coleções podem / devem ser usadas para manter uma lista classificada em Java. Eu tentei Mape Set, mas eles não eram o que eu estava procurando.

b4hand
fonte

Respostas:

187

Isso chega muito tarde, mas há uma classe no JDK apenas com a finalidade de ter uma lista classificada. É nomeado (um pouco fora de ordem com as outras Sorted*interfaces) " java.util.PriorityQueue". Ele pode classificar Comparable<?>s ou usar umComparator .

A diferença com o Listuso ordenado Collections.sort(...)é que isso manterá uma ordem parcial o tempo todo, com o desempenho de inserção O (log (n)), usando uma estrutura de dados de heap, enquanto a inserção em um ordenado ArrayListserá O (n) (ou seja, usando pesquisa binária e mover).

No entanto, ao contrário de a List, PriorityQueuenão suporta acesso indexado ( get(5)), a única maneira de acessar itens em um heap é retirá-los, um de cada vez (portanto, o nome PriorityQueue).

Martin Probst
fonte
4
imo esta resposta merece mais
votos positivos
96
No Javadoc: "Não é garantido que o Iterator fornecido no método iterator () atravesse os elementos do PriorityQueue em qualquer ordem específica."
Christoffer Hammarström
10
@iraff: uma fila de prioridade é apenas isso, uma estrutura de dados que é muito eficiente para manter uma fila de prioridade. Você pode pesquisar de frente e obter os itens de dados em ordem classificada. No entanto, os heaps não mantêm uma ordem total de elementos internamente (é por isso que eles são tão eficientes); portanto, não há como acessar elementos em ordem sem executar a operação de pesquisa.
Martin Probst
2
@ chrispy, depende um pouco do que você exatamente deseja alcançar com sua estrutura de dados. Os montões são uma maneira eficiente de manter uma lista de itens e recuperá-los de maneira ordenada posteriormente - o uso do iterador não funciona, mas se você fizer uma pesquisa com ele, os dados serão ordenados. A recuperação é destrutiva, mas isso ainda pode ser bom em muitos casos. Então eu acho que essa resposta é boa.
Martin Probst
4
@MartinProbst Retifique sua resposta para LIMPAR indicar que essa coleção NÃO PODE SER ITERADA na ordem esperada. Como muitos já disseram, isso é extremamente enganador!
Jorge Galvão
53

TreeMap e TreeSet fornecerão uma iteração sobre o conteúdo em ordem classificada. Ou você pode usar um ArrayList e usar Collections.sort () para classificá-lo. Todas essas classes estão em java.util

Michael Borgwardt
fonte
27
No entanto, existem duas desvantagens principais: a primeira é que um conjunto não pode ter duplicatas. A segunda é que, se você usar uma lista e Collections.sort (), tende a classificar constantemente listas enormes e apresenta um desempenho ruim. Claro que você pode usar uma bandeira 'suja', mas não é a mesma coisa.
Jeach
Isso leva a uma questão de saber se temos uma opção em Java que permite dupliactes e também dar performances semelhantes a Set (Equilibrado árvore binária)
mankadnandan
32

Se você deseja manter uma lista classificada que você modifica frequentemente (ou seja, uma estrutura que, além de ser classificada, permite duplicatas e cujos elementos podem ser referenciados com eficiência pelo índice), use um ArrayList, mas quando precisar inserir um elemento , sempre use Collections.binarySearch () para determinar o índice no qual você adiciona um determinado elemento. O último método informa o índice que você precisa inserir para manter sua lista na ordem classificada.

Neil Coffey
fonte
11
n inserções serão O (n ^ 2). Um TreeSet fornece código mais limpo e O (n log n). OTOH, para modificações infreqüentes, a pesquisa binária de uma matriz será mais rápida e usará menos memória (menos sobrecarga do GC).
Tom Hawtin - tackline 6/01/09
Para manter o código limpo e ainda permitir duplicatas, seria fácil criar uma classe SortedList que insira valores na ordem classificada.
Mr. Shiny and New #
Essa é uma resposta muito melhor do que a resposta mais votada, se você puder viver com a ressalva mencionada por @ TomHawtin-tackline. O fato de o iterador funcionar como esperado é crucial para a maioria dos casos.
DuneCat 11/10/12
Aliás, Tom está certo nesse comportamento específico: um conjunto de árvores fornecerá modificações mais eficientes. Mas um conjunto de árvores não é uma lista (no sentido estrito de ter elementos referenciados pelo índice e permitir duplicatas) e o pôster dizia que eles queriam uma lista.
21412 Neil Coffey
Obrigado Neil, isso foi demais!
vikingsteve
31

Use a classe TreeMultiset do Google Guava . O Goiaba possui uma API espetacular de coleções.

Um problema ao fornecer uma implementação de List que mantém a ordem classificada é a promessa feita nos JavaDocs do add()método.

jtb
fonte
Parabéns pela sugestão multiset
darthtrevino
5
+1 por mencionar o requisito que a Listsempre adiciona no final.
Roland Illig
2
Observe que o TreeMultiSet ainda não permite elementos duplicados (elementos que compareTo () retornam 0, não é igual a () verificação). Se você tende a adicionar mais de um item da mesma prioridade, isso aumentará a contagem do primeiro item adicionado, descartando o outro e efetivamente sendo uma boa implementação da Bolsa de Contagem.
bekce 28/09/12
12

Você deseja as implementações SortedSet , ou seja, TreeSet .

cleto
fonte
10
Não necessariamente; conjuntos não podem ter valores duplicados. Depende dos requisitos do OP.
Zach Langley
12

Existem algumas opções. Sugiro TreeSet se você não quiser duplicatas e os objetos que você está inserindo são comparáveis.

Você também pode usar os métodos estáticos da classe Collections para fazer isso.

Consulte Coleções # sort (java.util.List) e TreeSet para obter mais informações.

BenM
fonte
10

Se você quiser apenas classificar uma lista, use qualquer tipo de Lista e use Collections.sort () . Se você deseja garantir que os elementos da lista sejam exclusivos e sempre classificados, use um SortedSet .

Guillaume
fonte
5

O que eu fiz foi implementar a List com uma instância interna com todos os métodos delegados.

 public class ContactList implements List<Contact>, Serializable {
    private static final long serialVersionUID = -1862666454644475565L;
    private final List<Contact> list;

public ContactList() {
    super();
    this.list = new ArrayList<Contact>();
}

public ContactList(List<Contact> list) {
    super();
    //copy and order list
    List<Contact>aux= new ArrayList(list);
    Collections.sort(aux);

    this.list = aux;
}

public void clear() {
    list.clear();
}

public boolean contains(Object object) {
    return list.contains(object);
}

Depois, implementei um novo método "putOrdered", que é inserido na posição correta se o elemento não existir ou substituir apenas no caso de existir.

public void putOrdered(Contact contact) {
    int index=Collections.binarySearch(this.list,contact);
    if(index<0){
        index= -(index+1);
        list.add(index, contact);
    }else{
        list.set(index, contact);
    }
}

Se você deseja permitir elementos repetidos, basta implementar addOrdered (ou ambos).

public void addOrdered(Contact contact) {
    int index=Collections.binarySearch(this.list,contact);
    if(index<0){
        index= -(index+1);
    }
    list.add(index, contact);
}

Se você deseja evitar inserções, também pode lançar uma exceção de operação não suportada nos métodos "adicionar" e "definir".

public boolean add(Contact object) {
    throw new UnsupportedOperationException("Use putOrdered instead");
}

... e também É preciso ter cuidado com os métodos ListIterator, pois eles podem modificar sua lista interna. Nesse caso, você pode retornar uma cópia da lista interna ou lançar uma exceção novamente.

public ListIterator<Contact> listIterator() {
    return (new ArrayList<Contact>(list)).listIterator();
}
Carlos Verdes
fonte
O problema é que isso viola o Listcontrato. Talvez seja melhor implementar apenas Collection. E se ContactListfor classificado, contains()poderá ser implementado usando binarySearchtambém para ser mais eficiente.
Marcono1234 13/07/19
5

A maneira mais eficiente de implementar uma lista classificada como você deseja seria implementar um skiplist indexável como aqui: Wikipedia: skiplist indexável . Permitiria ter inserções / remoções em O (log (n)) e permitiria ter acesso indexado ao mesmo tempo. E também permitiria duplicatas.

O skiplist é uma estrutura de dados bastante interessante e, eu diria, subestimada. Infelizmente, não há implementação de skiplist indexada na biblioteca base Java, mas você pode usar uma das implementações de código aberto ou implementá-lo você mesmo. Existem implementações regulares do Skiplist como ConcurrentSkipListSet e ConcurrentSkipListMap

vladich
fonte
4

TreeSet não funcionaria porque eles não permitem duplicatas e não fornecem o método para buscar o elemento na posição específica. PriorityQueue não funcionaria porque não permite buscar elementos em uma posição específica, que é um requisito básico para uma lista. Eu acho que você precisa implementar seu próprio algoritmo para manter uma lista classificada em Java com tempo de inserção O (logn), a menos que você não precise de duplicatas. Talvez uma solução possa estar usando um TreeMap, onde a chave é uma subclasse do item, substituindo o método equals, para que duplicatas sejam permitidas.

Giuseppe
fonte
4

Usando o LambdaJ

Você pode tentar resolver essas tarefas com o LambdaJ se estiver usando versões anteriores do java 8. Você pode encontrá-lo aqui: http://code.google.com/p/lambdaj/

Aqui você tem um exemplo:

Classificar Iterativo

List<Person> sortedByAgePersons = new ArrayList<Person>(persons);
Collections.sort(sortedByAgePersons, new Comparator<Person>() {
        public int compare(Person p1, Person p2) {
           return Integer.valueOf(p1.getAge()).compareTo(p2.getAge());
        }
}); 

Classificar com LambdaJ

List<Person> sortedByAgePersons = sort(persons, on(Person.class).getAge()); 

É claro que esse tipo de beleza afeta o desempenho (em média duas vezes), mas você consegue encontrar um código mais legível?

Classificar com java 8 usando a expressão lambda

Collections.sort(persons, (p1, p2) -> p1.getAge().compareTo(p2.getAge()));
//or
persons.sort((p1, p2) -> p1.getAge().compareTo(p2.getAge()));
Federico Piazza
fonte
No seu último exemplo com a expressão java 8 lambda, como uma ordem inversa?
Rajat Shah
1
@RajatShah Eu acho que você pode fazer #-(p1.getAge().compareTo(p2.getAge()))
Federico Piazza
@RajatShah Fico feliz em ajudar
Federico Piazza
2

O problema com o PriorityQueue é que ele é copiado por uma matriz simples, e a lógica que coloca os elementos em ordem é feita pelos itens "fila [2 * n + 1] e fila [2 * (n + 1)]" ". Funciona muito bem se você simplesmente puxa da cabeça, mas a torna inútil se você estiver tentando chamar o .toArray em algum momento.

Eu resolvo esse problema usando com.google.common.collect.TreeMultimap, mas forneço um Comparador personalizado para os valores, agrupados em um Pedido, que nunca retorna 0.

ex. para o dobro:

private static final Ordering<Double> NoEqualOrder = Ordering.from(new Comparator<Double>() {

    @Override
    public int compare(Double d1, Double d2)
    {
        if (d1 < d2) {
            return -1;
        }
        else {
            return 1;
        }
    }
});

Dessa forma, eu obtenho os valores em ordem quando chamo .toArray () e também tenho duplicatas.

Martin Klosi
fonte
1

O que você quer é uma árvore de pesquisa binária. Ele mantém a ordem classificada e oferece acesso logarítmico para pesquisas, remoções e inserções (a menos que você tenha uma árvore degenerada - ela é linear). É muito fácil de implementar e você pode fazê-lo implementar a interface de lista, mas o acesso ao índice fica complicado.

A segunda abordagem é ter uma implementação ArrayList e, em seguida, uma classificação de bolha. Como você está inserindo ou removendo um elemento de cada vez, os tempos de acesso para inserções e remoções são lineares. As pesquisas são constantes logarítmicas e de acesso ao índice (os tempos podem ficar diferentes para o LinkedList). O único código que você precisa é de 5, talvez 6 linhas de classificação de bolhas.

Jakub Zaverka
fonte
1

Você pode usar Arraylist e Treemap, pois disse que deseja valores repetidos e não pode usar o TreeSet, embora ele também esteja classificado, mas é necessário definir o comparador.


fonte
1

Para Set, você pode usar o TreeSet. TreeSet ordena seus elementos com base em uma ordem natural ou em qualquer ordem de classificação passada para o Comparável para esse objeto em particular. Para o mapa, use o TreeMap. O TreeMap fornece a classificação das chaves. Para adicionar um objeto como uma chave ao TreeMap, essa classe deve implementar uma interface comparável que, por sua vez, força a implementação do método compare to () que contém a definição da ordem de classificação. http://techmastertutorial.in/java-collection-impl.html

Avi Tyagi
fonte
0

Use o método sort () para classificar a lista como abaixo:

List list = new ArrayList();

//add elements to the list

Comparator comparator = new SomeComparator();

Collections.sort(list, comparator);

Para referência, consulte o link: http://tutorials.jenkov.com/java-collections/sorting.html

Shashank Mungantiwar
fonte
0

Use o TreeSetque fornece elementos em ordem classificada. OU use Collection.sort()para classificação externa com Comparator().

Hari
fonte
TreeSet não permite duplicação de elementos. Às vezes, é um recurso desejado, outros não. Considerando o OP tentou os sets, eu acho que para o não
ΕΨΗΕΛΩΝ usr-local-
Não se quer dizer, ponto a TreeMap também: docs.oracle.com/javase/10/docs/api/java/util/TreeMap.html
Haakon Løtveit
0
import java.util.TreeSet;

public class Ass3 {
    TreeSet<String>str=new TreeSet<String>();
    str.add("dog");
    str.add("doonkey");
    str.add("rat");
    str.add("rabbit");
    str.add("elephant");
    System.out.println(str);    
}
Deepica MY
fonte
0

com o Java 8 Comparator, se quisermos classificar a lista, aqui estão as 10 cidades mais populosas do mundo e queremos classificá-la por nome, conforme relatado pela Time. Osaka, Japão. ... Cidade do México, México. ... Pequim, China. ... São Paulo, Brasil. ... Mumbai, índia. ... Xangai, China. ... Delhi, Índia. ... Tóquio, Japão.

 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.List;

public class SortCityList {

    /*
     * Here are the 10 most populated cities in the world and we want to sort it by
     * name, as reported by Time. Osaka, Japan. ... Mexico City, Mexico. ...
     * Beijing, China. ... São Paulo, Brazil. ... Mumbai, India. ... Shanghai,
     * China. ... Delhi, India. ... Tokyo, Japan.
     */
    public static void main(String[] args) {
        List<String> cities = Arrays.asList("Osaka", "Mexico City", "São Paulo", "Mumbai", "Shanghai", "Delhi",
                "Tokyo");
        System.out.println("Before Sorting List is:-");
        System.out.println(cities);
        System.out.println("--------------------------------");

        System.out.println("After Use of List sort(String.CASE_INSENSITIVE_ORDER) & Sorting List is:-");
        cities.sort(String.CASE_INSENSITIVE_ORDER);
        System.out.println(cities);
        System.out.println("--------------------------------");
        System.out.println("After Use of List sort(Comparator.naturalOrder()) & Sorting List is:-");
        cities.sort(Comparator.naturalOrder());
        System.out.println(cities);

    }

}
Vipul Gulhane
fonte
0

Classificando um ArrayList de acordo com os critérios definidos pelo usuário.

Classe de modelo

 class Student 
 { 
     int rollno; 
     String name, address; 

     public Student(int rollno, String name, String address) 
     { 
         this.rollno = rollno; 
         this.name = name; 
         this.address = address; 
     }   

     public String toString() 
     { 
         return this.rollno + " " + this.name + " " + this.address; 
     } 
 } 

Classe de classificação

 class Sortbyroll implements Comparator<Student> 
 {         
     public int compare(Student a, Student b) 
     { 
         return a.rollno - b.rollno; 
     } 
 } 

Classe principal

 class Main 
 { 
     public static void main (String[] args) 
     { 
         ArrayList<Student> ar = new ArrayList<Student>(); 
         ar.add(new Student(111, "bbbb", "london")); 
         ar.add(new Student(131, "aaaa", "nyc")); 
         ar.add(new Student(121, "cccc", "jaipur")); 

         System.out.println("Unsorted"); 
         for (int i=0; i<ar.size(); i++) 
             System.out.println(ar.get(i)); 

         Collections.sort(ar, new Sortbyroll()); 

         System.out.println("\nSorted by rollno"); 
         for (int i=0; i<ar.size(); i++) 
             System.out.println(ar.get(i)); 
     } 
 } 

Resultado

 Unsorted
 111 bbbb london
 131 aaaa nyc
 121 cccc jaipur

 Sorted by rollno
 111 bbbb london
 121 cccc jaipur
 131 aaaa nyc
skpaik
fonte