Raccolta ordinata in Java


161

Sono un principiante in Java. Si prega di suggerire quali raccolte possono / dovrebbero essere utilizzate per mantenere un elenco ordinato in Java. Ho provato Mape Set, ma non erano quello che stavo cercando.

Risposte:


188

Questo arriva molto tardi, ma c'è una classe nel JDK solo allo scopo di avere un elenco ordinato. È chiamato (un po 'fuori servizio con le altre Sorted*interfacce) " java.util.PriorityQueue". Può ordinare sia Comparable<?>s che usando a Comparator.

La differenza con un Listuso ordinato Collections.sort(...)è che ciò manterrà sempre un ordine parziale, con prestazioni di inserimento O (log (n)), utilizzando una struttura di dati heap, mentre l'inserimento in un ordine ArrayListsarà O (n) (cioè, usando la ricerca e lo spostamento binari).

Tuttavia, a differenza di a List, PriorityQueuenon supporta l'accesso indicizzato ( get(5)), l'unico modo per accedere agli elementi in un heap è di eliminarli, uno alla volta (da cui il nome PriorityQueue).


4
imo questa risposta merita più voti dal momento che indica l'unica raccolta che ha la capacità in JDK
nimcap

96
Da Javadoc: "L'iteratore fornito nel metodo iteratore () non è garantito per attraversare gli elementi di PriorityQueue in un ordine particolare."
Christoffer Hammarström

10
@giraff: una coda di priorità è proprio questa, una struttura di dati che è molto efficiente nel mantenere una coda di priorità. Puoi eseguire il polling dalla parte anteriore e ottenere gli elementi di dati in ordine ordinato. Tuttavia, gli heap non mantengono un ordine totale di elementi internamente (ecco perché sono così efficienti), quindi non c'è modo di accedere agli elementi in ordine senza eseguire l'operazione di polling.
Martin Probst,

2
@chrispy dipende un po 'da ciò che esattamente vuoi fare con la tua struttura dati. Gli heap sono un modo efficiente per mantenere un elenco di elementi e recuperarli in un modo ordinato in seguito: l'utilizzo dell'iteratore non funziona, ma se si esegue il polling da esso, i dati verranno ordinati. Il recupero è distruttivo, ma in molti casi potrebbe comunque andare bene. Quindi penso che questa risposta sia buona.
Martin Probst,

4
@MartinProbst Correggi la tua risposta per indicare CHIARAMENTE che la raccolta NON PUO 'ESSERE ITERATA nell'ordine previsto. Come molti hanno già detto, questo è estremamente fuorviante!
Jorge Galvão,

53

TreeMap e TreeSet ti daranno un'iterazione sul contenuto in ordine ordinato. Oppure potresti usare un ArrayList e usare Collections.sort () per ordinarlo. Tutte quelle classi sono in java.util


27
Ci sono due principali svantaggi di questo, il primo è che un Set non può avere duplicati. Il secondo è che se si utilizza un elenco e Collections.sort (), si tende a ordinare costantemente elenchi enormi e si ottengono prestazioni scadenti. Sicuramente puoi usare una bandiera "sporca", ma non è esattamente la stessa.
Jeach,

Ciò porta a chiedersi se abbiamo un'opzione in Java che consente dupliactes e offre anche prestazioni simili a Set (Balanced Binary Tree)
mankadnandan,

32

Se desideri mantenere un elenco ordinato che modificherai frequentemente (ad esempio una struttura che, oltre a essere ordinata, consente i duplicati e i cui elementi possono essere referenziati in modo efficiente dall'indice), usa un ArrayList ma quando devi inserire un elemento , utilizza sempre Collections.binarySearch () per determinare l'indice a cui aggiungi un determinato elemento. Quest'ultimo metodo indica l'indice che è necessario inserire per mantenere l'elenco in ordine.


11
n inserimenti sarà O (n ^ 2). Un TreeSet ti fornirà codice più pulito e O (n log n). OTOH, per una modifica rara la ricerca binaria di un array sarà più veloce e utilizzerà meno memoria (quindi meno sovraccarico GC).
Tom Hawtin: affronta il

Per mantenere pulito il codice e consentire comunque i duplicati, sarebbe abbastanza facile creare una classe SortedList che inserisce i valori in ordine ordinato.
Mr. Shiny e New

Questa è una risposta molto migliore della risposta più votata, imo, se riesci a convivere con l'avvertimento menzionato da @ TomHawtin-tackline. Che l'iteratore funzioni come previsto è fondamentale per la maggior parte dei casi.
DuneCat,

Per inciso, Tom ha ragione su quel comportamento specifico: un set di alberi ti darà modifiche più efficienti. Ma un insieme di alberi non è un elenco (nel senso più stretto di avere elementi referenziati dall'indice e consentire duplicati) e il poster ha affermato di volere un elenco.
Neil Coffey,

Grazie Neil, è stato fantastico!
vikingsteve,

31

Utilizza la classe TreeMultiset di Google Guava . Guava ha una spettacolare API per le raccolte.

Un problema nel fornire un'implementazione dell'elenco che mantiene l'ordine ordinato è la promessa fatta nei metodi JavaDocs del add()metodo.


Complimenti per il suggerimento multiset
darthtrevino,

5
+1 per menzionare il requisito che a Listaggiunge sempre alla fine.
Roland Illig,

2
Si noti che TreeMultiSet non consente ancora elementi duplicati (elementi che compareTo () restituisce 0, non equals () check). Se si tende ad aggiungere più di un elemento con la stessa priorità, aumenterà solo il conteggio del primo aggiunto, scartando l'altro ed efficacemente una buona implementazione di Counting Bag.
Bekce,


12

Ci sono alcune opzioni Suggerirei TreeSet se non vuoi duplicati e gli oggetti che stai inserendo sono comparabili.

A tale scopo, puoi anche utilizzare i metodi statici della classe Collections.

Vedi Collezioni # sort (java.util.List) e TreeSet per maggiori informazioni.



5

Quello che ho fatto è implementare List con un'istanza interna con tutti i metodi delegati.

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

Dopo, ho implementato un nuovo metodo "putOrdered" che si inserisce nella posizione corretta se l'elemento non esiste o lo sostituisce nel caso esistesse.

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 si desidera consentire elementi ripetuti, implementare invece addOrdered (o entrambi).

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

Se si desidera evitare inserimenti, è anche possibile generare un'eccezione di operazione non supportata sui metodi "aggiungi" e "imposta".

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

... e devi anche stare attento con i metodi ListIterator perché potrebbero modificare il tuo elenco interno. In questo caso è possibile restituire una copia dell'elenco interno o nuovamente generare un'eccezione.

public ListIterator<Contact> listIterator() {
    return (new ArrayList<Contact>(list)).listIterator();
}

Il problema è che questo viola il Listcontratto. Forse sarebbe meglio solo implementare Collection. E se ContactListè ordinato, allora contains()può essere implementato usando binarySearchanche per essere più efficiente.
Marcono1234,

5

Il modo più efficace per implementare un elenco ordinato come desiderato sarebbe quello di implementare uno skiplist indicizzabile come qui: Wikipedia: skiplist indicizzabile . Consentirebbe di avere inserimenti / rimozioni in O (log (n)) e consentirebbe di avere accesso indicizzato allo stesso tempo. E consentirebbe anche duplicati.

Skiplist è una struttura dati piuttosto interessante e, direi, sottovalutata. Purtroppo non esiste un'implementazione skiplist indicizzata nella libreria di base Java, ma è possibile utilizzare una delle implementazioni open source o implementarla da soli. Esistono implementazioni regolari di Skiplist come ConcurrentSkipListSet e ConcurrentSkipListMap


4

TreeSet non funzionerebbe perché non consente duplicati e non fornisce il metodo per recuperare l'elemento in una posizione specifica. PriorityQueue non funzionerebbe perché non consente il recupero di elementi in una posizione specifica, che è un requisito di base per un elenco. Penso che sia necessario implementare il proprio algoritmo per mantenere un elenco ordinato in Java con tempo di inserimento O (logn), a meno che non siano necessari duplicati. Forse una soluzione potrebbe essere l'utilizzo di una TreeMap in cui la chiave è una sottoclasse dell'elemento che sovrascrive il metodo equals in modo da consentire i duplicati.


4

Usando LambdaJ

Puoi provare a risolvere queste attività con LambdaJ se stai utilizzando versioni precedenti a java 8. Puoi trovarlo qui: http://code.google.com/p/lambdaj/

Ecco un esempio:

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

Ordina con LambdaJ

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

Certo, avere questo tipo di bellezza influisce sulla performance (in media 2 volte), ma riesci a trovare un codice più leggibile?

Ordina con java 8 usando l'espressione lambda

Collections.sort(persons, (p1, p2) -> p1.getAge().compareTo(p2.getAge()));
//or
persons.sort((p1, p2) -> p1.getAge().compareTo(p2.getAge()));

Nel tuo ultimo esempio con l'espressione lambda java 8, come si può invertire l'ordinamento?
Rajat Shah,

1
@RajatShah Immagino che tu possa semplicemente fare-(p1.getAge().compareTo(p2.getAge()))
Federico Piazza,

@RajatShah felice di aiutarti
Federico Piazza

2

Il problema con PriorityQueue è che viene eseguito il backup da un semplice array e la logica che ottiene gli elementi in ordine viene eseguita dalla cosa "coda [2 * n + 1] e coda [2 * (n + 1)]". Funziona alla grande se si estrae semplicemente dalla testa, ma lo rende inutile se si sta cercando di richiamare il .toArray su di esso ad un certo punto.

Risolvo questo problema utilizzando com.google.common.collect.TreeMultimap, ma fornisco un comparatore personalizzato per i valori, racchiuso in un ordinamento, che non restituisce mai 0.

ex. per doppio:

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

In questo modo ottengo i valori in ordine quando chiamo .toArray () e ho anche dei duplicati.


1

Quello che vuoi è un albero di ricerca binario. Mantiene l'ordine ordinato offrendo al contempo l'accesso logaritmico per ricerche, rimozioni e inserimenti (a meno che tu non abbia un albero degenerato - quindi è lineare). È abbastanza facile da implementare e puoi persino farlo implementare l'interfaccia Elenco, ma l'accesso all'indice diventa complicato.

Il secondo approccio consiste nell'avere un ArrayList e quindi un'implementazione di ordinamento a bolle. Poiché si sta inserendo o rimuovendo un elemento alla volta, i tempi di accesso per inserimenti e rimozioni sono lineari. Le ricerche sono logaritmiche e costanti di accesso all'indice (i tempi possono essere diversi per LinkedList). L'unico codice che ti serve è 5, forse 6 righe di ordinamento a bolle.


1

Puoi usare Arraylist e Treemap, poiché hai detto che vuoi anche valori ripetuti, quindi non puoi usare TreeSet, anche se è ordinato, ma devi definire un comparatore.


1

Per Set puoi usare TreeSet. TreeSet ordina i suoi elementi sulla base di un ordinamento naturale o di qualsiasi ordinamento passato al Comparabile per quel particolare oggetto. Per la mappa usa TreeMap. TreeMap fornisce l'ordinamento tramite chiavi. Per aggiungere un oggetto come chiave alla TreeMap quella classe dovrebbe implementare un'interfaccia comparabile che a sua volta impone di implementare il metodo compare to () che contiene la definizione del criterio di ordinamento. http://techmastertutorial.in/java-collection-impl.html




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

0

con Java 8 Comparator, se vogliamo ordinare l'elenco allora Ecco le 10 città più popolate al mondo e vogliamo ordinarlo per nome, come riportato da Time. Osaka, Giappone. ... Città del Messico, Messico. ... Pechino, Cina. ... San Paolo, Brasile. ... Mumbai, India. ... Shanghai, Cina. ... Delhi, India. ... Tokyo, Giappone.

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

    }

}

0

Ordinamento di un ArrayList in base a criteri definiti dall'utente.

Classe del modello

 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 di classificazione

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

Classe principale

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

Produzione

 Unsorted
 111 bbbb london
 131 aaaa nyc
 121 cccc jaipur

 Sorted by rollno
 111 bbbb london
 121 cccc jaipur
 131 aaaa nyc
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.