web-dev-qa-db-fra.com

Implémentation de liste qui maintient l'ordre

Existe-t-il une implémentation List existante dans Java qui maintient l'ordre en fonction de Comparator fourni?

Quelque chose qui peut être utilisé de la manière suivante:

Comparator<T> cmp = new MyComparator<T>();
List<T> l = new OrderedList<T>(cmp);
l.add(someT);

de sorte que someT soit inséré de telle sorte que l'ordre dans la liste soit maintenu selon cmp

(Sur la suggestion de @andersoj, je termine ma question avec une autre demande)

Je veux également pouvoir parcourir la liste dans l'ordre trié sans supprimer les éléments, à savoir:

T min = Const.SMALLEST_T;
for (T e: l) {
  assertTrue(cmp.compare(min, e) >= 0);
  min = e;
}

devrait passer.

Toutes les suggestions sont les bienvenues (sauf me dire d'utiliser Collections.sort sur la liste complète non ordonnée), cependant, je préférerais quelque chose dans Java.* ou éventuellement org.Apache.* car il serait difficile d'introduire de nouvelles bibliothèques en ce moment.

Remarque: (UPDATE4) J'ai réalisé que les implémentations de ce type de liste auraient des performances inadéquates. Il existe deux approches générales:

  1. Utiliser la structure liée (sorte de) B-tree ou similaire
  2. Utiliser le tableau et l'insertion (avec recherche binaire)

Non 1. a un problème avec le cache du CPU manqué Non 2. a un problème avec le déplacement des éléments dans le tableau.

UPDATE2:TreeSet ne fonctionne pas car il utilise le comparateur fourni (MyComparator) pour vérifier l'égalité et sur la base de cela suppose que les éléments sont égaux et les exclure. J'ai besoin de ce comparateur uniquement pour la commande, pas pour le filtrage "d'unicité" (puisque les éléments par leur ordre naturel ne sont pas égaux)

UPDATE3:PriorityQueue ne fonctionne pas comme List (selon mes besoins) car il n'y a aucun moyen de le parcourir dans l'ordre où il est "trié", pour obtenir les éléments dans l'ordre trié, vous devez les supprimer de la collection.

METTRE À JOUR:

Question similaire:
ne bonne liste triée pour Java
Liste des tableaux triés en Java

16
Op De Cirkel

Réponse à une nouvelle exigence. Je vois deux potentiels:

  • Faites ce que dit le JavaDoc pour PriorityQueue:

    Cette classe et son itérateur implémentent toutes les méthodes facultatives des interfaces Collection et Iterator. L'itérateur fourni dans la méthode iterator() n'est pas garanti pour parcourir les éléments de la file d'attente prioritaire dans un ordre particulier. Si vous avez besoin d'une traversée ordonnée, pensez à utiliser Arrays.sort(pq.toArray()).

    Je soupçonne que cela donnera les meilleures performances compte tenu de vos besoins. Si cela n'est pas acceptable, vous devrez mieux expliquer ce que vous essayez d'accomplir.

  • Construisez une liste qui se trie simplement lors de l'ajout de nouveaux éléments. C'est une vraie douleur ... si vous avez utilisé une structure liée, vous pouvez faire un tri par insertion efficace, mais la localité est mauvaise. Si vous avez utilisé une structure basée sur un tableau, le tri par insertion est difficile mais la traversée est meilleure. Si l'itération/la traversée est peu fréquente, vous pouvez conserver le contenu de la liste non trié et trier uniquement à la demande.

  • Envisagez d'utiliser un PriorityQueue comme je l'ai suggéré, et dans le cas où vous avez besoin d'itérer dans l'ordre, écrivez un itérateur wrapper:

    class PqIter implements Iterator<T>
    {
       final PriorityQueue<T> pq;
       public PqIter(PriorityQueue <T> source)
       {
         pq = new PriorityQueue(source); 
       }
    
       @Override
       public boolean hasNext()
       {
         return pq.peek() != null
       }
    
       @Override
       public T next()
       { return pq.poll(); }
    
       @Override
       public void remove()
       { throw new UnsupportedOperationException(""); }
    }
    
  • Utilisez la goyave TreeMultiSet . J'ai testé le code suivant avec Integer et il semble faire la bonne chose.

    import com.google.common.collect.TreeMultiset;
    
    public class TreeMultiSetTest { 
      public static void main(String[] args) {
        TreeMultiset<Integer> ts = TreeMultiset.create();
        ts.add(1);  ts.add(0); ts.add(2);
        ts.add(-1); ts.add(5); ts.add(2);
    
        for (Integer i : ts) {
          System.out.println(i);
        } 
      } 
    }
    

Ce qui suit résout le problème d'unicité/filtrage que vous rencontriez lors de l'utilisation d'un SortedSet. Je vois que vous voulez aussi un itérateur, donc ça ne marchera pas.

Si ce que vous voulez vraiment est une chose ordonnée semblable à une liste , vous pouvez utiliser un PriorityQueue .

Comparator<T> cmp = new MyComparator<T>();
PriorityQueue<T> pq = new PriorityQueue<T>(cmp);
pq.add(someT);

Prenez note de ce que la documentation de l'API dit sur les propriétés temporelles de diverses opérations:

Note d'implémentation: cette implémentation fournit O (log (n)) le temps pour les méthodes de compression et de compression (offer, poll, remove() et add); temps linéaire pour les méthodes remove(Object) et contains(Object); et temps constant pour les méthodes de récupération (peek, element et size).

Vous devez également savoir que les itérateurs produits par PriorityQueue ne se comportent pas comme on pourrait s'y attendre:

Le Iterator fourni dans la méthode iterator() n'est pas garanti pour parcourir les éléments de la file d'attente prioritaire dans un ordre particulier. Si vous avez besoin d'une traversée ordonnée, pensez à utiliser Arrays.sort(pq.toArray()).

Je viens de remarquer que Guava fournit un MinMaxPriorityQueue . Cette implémentation est basée sur un tableau, plutôt que la forme liée fournie dans le PriorityQueue du JDK, et a donc probablement un comportement temporel différent. Si vous faites quelque chose de sensible aux performances, vous voudrez peut-être y jeter un œil. Bien que les notes donnent des temps big-O (linéaires et logarithmiques) légèrement différents, tous ces temps doivent également être bornés, ce qui peut être utile.

Il n'y a pas d'implémentation de List en soi qui maintient l'ordre, mais ce que vous recherchez probablement, ce sont des implémentations de SortedSet . A TreeSet est le plus courant. L'autre implémentation, un ConcurrentSkipListSet est pour des utilisations plus spécifiques. Notez qu'un SortedSet fournit un ordre, mais n'autorise pas les entrées en double, tout comme un List.

Réfs:

9
andersoj

Vous devriez probablement utiliser un TreeSet :

Les éléments sont ordonnés en utilisant leur ordre naturel, ou par un comparateur fourni au moment de la création, en fonction du constructeur utilisé.

Exemple:

Comparator<T> cmp = new MyComparator<T>();
TreeSet<T> t = new TreeSet<T>(cmp);
l.add(someT);

Notez qu'il s'agit d'un ensemble , donc aucune entrée en double n'est autorisée. Cela peut ou non fonctionner pour votre cas d'utilisation spécifique.

17
beerbajay