web-dev-qa-db-fra.com

Pourquoi n'y a-t-il pas de SortedList en Java?

Dans Java, il existe les interfaces SortedSet et SortedMap. Les deux font partie du framework standard de collections Java et fournissent un moyen trié d'accéder aux éléments.

Cependant, à ma connaissance, il n’existe pas de SortedList en Java. Vous pouvez utiliser Java.util.Collections.sort() pour trier une liste.

Une idée pourquoi c'est conçu comme ça?

461
Jijoy

Les itérateurs de liste garantissent avant tout que vous obteniez les éléments de la liste dans l'ordre interne de la liste (aka. ordre d'insertion ). Plus précisément, c'est dans l'ordre dans lequel vous avez inséré les éléments ou sur la façon dont vous avez manipulé la liste. Le tri peut être vu comme une manipulation de la structure de données et il existe plusieurs façons de trier la liste.

Je vais classer les chemins dans l'ordre de utilité comme je le vois personnellement:

1. Pensez plutôt à utiliser les collections Set ou Bag

NOTE: J'ai mis cette option en haut car c'est ce que vous voulez normalement faire de toute façon.

Un ensemble trié trie automatiquement la collection lors de l'insertion , ce qui signifie qu'il effectue le tri pendant que vous ajoutez des éléments à la collection. Cela signifie également que vous n'avez pas besoin de le trier manuellement.

De plus, si vous êtes certain de ne pas avoir à vous soucier des éléments en double, vous pouvez utiliser plutôt TreeSet<T> . Il implémente les interfaces SortedSet et NavigableSet et fonctionne comme vous le souhaiteriez probablement d'une liste:

_TreeSet<String> set = new TreeSet<String>();
set.add("lol");
set.add("cat");
// automatically sorts natural order when adding

for (String s : set) {
    System.out.println(s);
}
// Prints out "cat" and "lol"
_

Si vous ne voulez pas l’ordre naturel, vous pouvez utiliser le paramètre constructeur qui prend un Comparator<T> .

Alternativement, vous pouvez utiliser Multisets (également appelé Sacs ) , c'est un Set qui autorise les éléments en double à la place et il existe une implémentation tierce de ceux-ci. Notamment dans les bibliothèques de Guava , il existe un TreeMultiset , qui fonctionne beaucoup comme le TreeSet.

2. Triez votre liste avec Collections.sort()

Comme mentionné ci-dessus, le tri de Lists est une manipulation de la structure de données. Donc, pour les situations où vous avez besoin d'une "source de vérité" qui sera triée de différentes manières, le tri manuel est la voie à suivre.

Vous pouvez trier votre liste avec la méthode Java.util.Collections.sort() . Voici un exemple de code sur la façon dont:

_List<String> strings = new ArrayList<String>()
strings.add("lol");
strings.add("cat");

Collections.sort(strings);
for (String s : strings) {
    System.out.println(s);
}
// Prints out "cat" and "lol"
_

Utiliser des comparateurs

Un avantage évident est que vous pouvez utiliser Comparator dans la méthode sort. Java fournit également certaines implémentations pour Comparator, telles que Collator , ce qui est utile pour les chaînes de tri sensibles à la localisation. Voici un exemple:

_Collator usCollator = Collator.getInstance(Locale.US);
usCollator.setStrength(Collator.PRIMARY); // ignores casing

Collections.sort(strings, usCollator);
_

Tri dans des environnements concurrents

Notez cependant que l’utilisation de la méthode sort n’est pas conviviale dans les environnements concurrents, car l’instance de collection sera manipulée et vous devriez envisager plutôt d’utiliser des collections immuables. C’est quelque chose que Guava fournit dans la classe Ordering et est simple:

_List<string> sorted = Ordering.natural().sortedCopy(strings);
_

3. Enveloppez votre liste avec _Java.util.PriorityQueue_

Bien qu'il n'y ait pas de liste triée dans Java, il existe cependant une file d'attente triée qui fonctionnerait probablement aussi bien pour vous. C'est la classe Java.util.PriorityQueue .

Nico Haase a lié dans les commentaires un question connexe qui répond également à cela.

Dans une collection triée , vous ne souhaitez probablement pas manipuler la structure de données interne. C'est pourquoi PriorityQueue n'implémente pas l'interface List (car cela vous donne un accès direct à ses éléments).

Mise en garde sur l'itérateur PriorityQueue

La classe PriorityQueue implémente les interfaces _Iterable<E>_ et _Collection<E>_ afin qu’elle puisse être itérée comme d’habitude. Toutefois, il n'est pas garanti que l'itérateur renvoie les éléments dans l'ordre de tri. Au lieu de cela (comme Alderath le souligne dans les commentaires), vous devez poll() la file d'attente jusqu'à ce qu'elle soit vide.

Notez que vous pouvez convertir une liste en file d'attente prioritaire via le constructeur prenant toute collection :

_List<String> strings = new ArrayList<String>()
strings.add("lol");
strings.add("cat");

PriorityQueue<String> sortedStrings = new PriorityQueue(strings);
while(!sortedStrings.isEmpty()) {
    System.out.println(sortedStrings.poll());
}
// Prints out "cat" and "lol"
_

4. Ecrivez votre propre classe SortedList

REMARQUE: Vous ne devriez pas avoir à faire cela.

Vous pouvez écrire votre propre classe List qui trie chaque fois que vous ajoutez un nouvel élément. Cela peut être assez lourd en calcul en fonction de votre implémentation et est inutile , à moins que vous ne vouliez le faire en tant qu’exercice, pour deux raisons principales:

  1. Il rompt le contrat de l'interface _List<E>_ car les méthodes add doivent garantir que l'élément réside dans l'index spécifié par l'utilisateur.
  2. Pourquoi réinventer la roue? Vous devriez utiliser TreeSet ou Multisets à la place, comme indiqué dans le premier point ci-dessus.

Cependant, si vous voulez le faire comme exercice, voici un exemple de code pour vous aider à démarrer, il utilise la classe abstraite AbstractList:

_public class SortedList<E> extends AbstractList<E> {

    private ArrayList<E> internalList = new ArrayList<E>();

    // Note that add(E e) in AbstractList is calling this one
    @Override 
    public void add(int position, E e) {
        internalList.add(e);
        Collections.sort(internalList, null);
    }

    @Override
    public E get(int i) {
        return internalList.get(i);
    }

    @Override
    public int size() {
        return internalList.size();
    }

}
_

Notez que si vous n'avez pas remplacé les méthodes dont vous avez besoin, les implémentations par défaut de AbstractList renverront UnsupportedOperationExceptions.

635
Spoike

Parce que le concept de liste est incompatible avec le concept de collection triée automatiquement. Le point essentiel d’une liste est qu’après avoir appelé list.add(7, elem), un appel à list.get(7) renverra elem. Avec une liste triée automatiquement, l'élément pourrait se retrouver dans une position arbitraire.

67
Michael Borgwardt

Étant donné que toutes les listes sont déjà "triées" en fonction de l'ordre auquel les éléments ont été ajoutés (ordre FIFO), vous pouvez les "ordonner" avec un autre ordre, y compris l'ordre naturel des éléments, en utilisant Java.util.Collections.sort().

MODIFIER:

Les listes en tant que structures de données sont basées sur ce qui est intéressant, c'est l'ordre dans lequel les éléments ont été insérés.

Les ensembles n'ont pas cette information.

Si vous souhaitez commander en ajoutant du temps, utilisez List. Si vous souhaitez commander selon d'autres critères, utilisez SortedSet.

25
SJuan76

Set et Map sont des structures de données non linéaires. La liste est une structure de données linéaire.

enter image description here


La structure de données arborescente SortedSet et SortedMap interfaces implémente TreeSet et TreeMap respectivement à l'aide de l'algorithme d'implémentation arbre rouge-noir utilisé. Donc, il s’assure qu’il n’ya pas d’éléments en double (ou de clés dans le cas de Map).

  • List maintient déjà une collection ordonnée et une structure de données basée sur un index, les arbres ne sont pas des structures de données basées sur un index.
  • Tree par définition, ne peut pas contenir de doublons.
  • Dans List nous pouvons avoir des doublons, il n’ya donc pas de TreeList (c’est-à-dire non SortedList).
  • La liste conserve les éléments dans l'ordre d'insertion. Donc, si nous voulons trier la liste, nous devons utiliser Java.util.Collections.sort(). Il trie la liste spécifiée en ordre croissant, en fonction de l'ordre naturel de ses éléments.
17
Premraj

JavaFX SortedList

Bien que cela ait pris un certain temps, Java 8 a un List trié. http://docs.Oracle.com/javase/8/javafx/api/javafx/collections/transformation/SortedList.html

Comme vous pouvez le constater dans les javadocs, il fait partie des collections JavaFX , destiné à fournir une vue triée sur une liste observable.

Mise à jour: notez qu'avec Java 11, le toolkit JavaFX s'est déplacé en dehors du JDK et constitue désormais une bibliothèque distincte. JavaFX 11 est disponible sous forme de SDK téléchargeable ou de MavenCentral. Voir https://openjfx.io

11
swpalmer

À compter d'avril 2015, pour tous les nouveaux arrivants, Android dispose désormais d'une classe SortedList dans la bibliothèque de support, conçue spécifiquement pour fonctionner avec RecyclerView. Voici le blog post à ce sujet.

9
Amagi82

Un autre point concerne la complexité temporelle des opérations d’insertion. Pour une insertion de liste, on s’attend à une complexité de O (1). Mais cela ne pourrait pas être garanti avec une liste triée.

Et le point le plus important est que les listes ne supposent rien sur leurs éléments. Par exemple, vous pouvez créer des listes d'éléments qui n'implémentent pas equals ou compare.

4
Ingo

Pensez-y comme ceci: l'interface List dispose de méthodes telles que add(int index, E element), set(int index, E element). Le contrat stipule qu’une fois que vous avez ajouté un élément à la position X, vous le trouverez à moins d’ajouter ou de supprimer des éléments auparavant.

Si une implémentation de liste stockait des éléments dans un ordre autre que celui basé sur l'index, les méthodes de la liste ci-dessus n'auraient aucun sens.

3
Costi Ciudatu

La première ligne de l'API List indique qu'il s'agit d'une collection ordonnée (également appelée séquence). Si vous triez la liste, vous ne pouvez pas maintenir l'ordre, il n'y a donc pas de TreeList en Java.
Comme le dit l'API Java La liste s'est inspirée de Séquence et affiche les propriétés de la séquence http://en.wikipedia.org/wiki/Sequence_ (mathématiques )

Cela ne signifie pas que vous ne pouvez pas trier la liste, mais Java est strict selon sa définition et ne fournit pas de version triée des listes par défaut.

2
Syam

Pensez à utiliser indexed-tree-map . TreeSet du JDK amélioré qui donne accès à chaque élément et permet de rechercher l'index d'un élément sans itération ni liste cachée sous-jacente sauvegardant l'arborescence. L'algorithme est basé sur la mise à jour des poids des nœuds changeants chaque fois qu'il y a un changement.

1
Vitaly Sazanovich

Si vous recherchez un moyen de trier les éléments, mais pouvez également y accéder par index de manière efficace, vous pouvez procéder comme suit:

  1. Utilisez une liste d'accès aléatoire pour le stockage (par exemple, ArrayList)
  2. Assurez-vous qu'il est toujours trié

Ensuite, pour ajouter ou supprimer un élément, vous pouvez utiliser Collections.binarySearch pour obtenir l'index d'insertion/suppression. Étant donné que votre liste implémente un accès aléatoire, vous pouvez modifier efficacement la liste avec l'index déterminé.

Exemple:

_/**
 * @deprecated
 *      Only for demonstration purposes. Implementation is incomplete and does not 
 *      handle invalid arguments.
 */
@Deprecated
public class SortingList<E extends Comparable<E>> {
    private ArrayList<E> delegate;

    public SortingList() {
        delegate = new ArrayList<>();
    }

    public void add(E e) {
        int insertionIndex = Collections.binarySearch(delegate, e);

        // < 0 if element is not in the list, see Collections.binarySearch
        if (insertionIndex < 0) {
            insertionIndex = -(insertionIndex + 1);
        }
        else {
            // Insertion index is index of existing element, to add new element 
            // behind it increase index
            insertionIndex++;
        }

        delegate.add(insertionIndex, e);
    }

    public void remove(E e) {
        int index = Collections.binarySearch(delegate, e);
        delegate.remove(index);
    }

    public E get(int index) {
        return delegate.get(index);
    }
}
_
0
Marcono1234