web-dev-qa-db-fra.com

Comment puis-je trier ce ArrayList comme je le souhaite?

Voici un programme de tri simple d'une ArrayList:

ArrayList<String> list = new ArrayList<String>();

list.add("1_Update");
list.add("11_Add");
list.add("12_Delete");
list.add("2_Create");

Collections.sort(list);
for (String str : list) {
  System.out.println(str.toString());
}

Je m'attendais à la sortie de ce programme en tant que:

1_Update
2_Create
11_Add
12_Delete

Mais quand je lance ce programme, je reçois une sortie en tant que:

11_Add
12_Delete
1_Update
2_Create

Pourquoi est-ce et comment puis-je obtenir le ArrayList pour trier comme indiqué dans la sortie attendue?

36
Pahari Chora

Vous pouvez écrire un comparateur personnalisé:

Collections.sort(list, new Comparator<String>() {
    public int compare(String a, String b) {
        return Integer.signum(fixString(a) - fixString(b));
    }
    private int fixString(String in) {
        return Integer.parseInt(in.substring(0, in.indexOf('_')));
    }
});
70
nsayer

Lorsque vous triez ce type de données sous forme de chaîne, il compare les caractères eux-mêmes, y compris les chiffres. Toutes les chaînes commençant par "1", par exemple, se retrouveront ensemble. Donc, la commande se termine comme ceci ... 

1 10 100 2 20 200

À aucun moment, le type "ne réalise" que vous attribuez une signification à des sous-ensembles de la chaîne, tels que les nombres de longueur variable au début de la chaîne. Lorsque vous triez les nombres sous forme de chaînes, il peut être utile de placer des zéros à gauche pour couvrir le plus grand nombre, mais cela ne résout pas vraiment le problème lorsque vous ne contrôlez pas les données, comme dans votre exemple. Dans ce cas, le genre serait ...

001 002 010 020 100 200

8
Jim Blake

Il est trié sous forme de texte (par ordre alphabétique) et non sous forme de nombre. Pour contourner ce problème, vous pouvez implémenter un comparateur personnalisé comme suggéré dans la réponse de nsayer.

6
Halvard

Il fait une comparaison lexicographique. Il compare le premier caractère de chaque chaîne en les triant. Il compare ensuite la deuxième chaîne de ceux ayant le même premier caractère. Lorsqu'il compare le caractère '_' à un nombre, sa valeur est supérieure à celle de tout caractère numérique unique, tout comme 8> 7 et un> 9. N'oubliez pas qu'il s'agit d'une comparaison de caractère et non d'une comparaison numérique.

Il existe des moyens d'implémenter votre propre routage de tri personnalisé, ce qui peut être préférable à renommer vos noms de script.

Si le renommage de vos noms de script est une option, cela peut permettre l'utilisation d'autres outils de script. Un format peut être

 01_create_table.sql 
 02_create_index.sql 
 11 .assign_privileges.sql 

En gardant vos deux premiers chiffres à deux caractères, la comparaison lexicographique fonctionnera.

3
QSmienk

La documentation de la méthode Collections.sort () dit:

Trie la liste spécifiée dans ordre croissant, selon le ordre naturel de ses éléments.

Ce qui signifie pour les chaînes que vous allez obtenir la liste par ordre alphabétique. String 11_assign_privileges.sql vient avant les chaînes 1_create_table.sql et 12_07_insert_static_data.sql vient avant 1_create_table.sql, etc. Ainsi, le programme fonctionne comme prévu.

2

Pour que Collection.sort () trie arbitrairement, vous pouvez utiliser

Collections.sort(List list, Comparator c)  

Ensuite, implémentez simplement un comparateur qui scinde la chaîne et effectue un tri en fonction du nombre, puis du reste ou de la manière souhaitée.

0
Kris

Tout le monde a déjà fait remarquer que l'explication tenait au fait que vos chaînes sont triées sous forme de chaînes et qu'un certain nombre ont déjà dirigé votre attention sur la comparaison de chaînes Natural Order. J'ajouterai simplement que l'écriture de ce comparateur est un excellent exercice et une excellente occasion de pratiquer un développement basé sur des tests. Je l'ai utilisé pour démontrer TDD au Code Camp; slides & code are here .

0
Carl Manaster

Comme indiqué ci-dessus, vous recherchez une implémentation de comparateur qui implémente une sorte naturelle. Jeff Atwood a écrit un excellent post sur le tri naturel il y a quelque temps - il vaut vraiment la peine d'être lu.

Si vous recherchez une implémentation Java, celle-ci est utile: http://www.davekoelle.com/alphanum.html

0
johnstok

Vous pouvez ajouter l'interface IComparable, puis trier selon une propriété spécifique. Si vous avez une collection d'articles d'un magasin, par exemple, vous souhaitez peut-être les trier par prix ou par catégorie, etc. Si vous souhaitez commander par nom, voici un exemple:

notez comment ArrayList est trié par la propriété name des éléments. Si vous n’ajoutez pas IComparable, l’utilisation d’une méthode de tri générera une erreur.

enter image description here

static void Main(string[] args)
    {
        ArrayList items = new ArrayList();
        items.Add(new Item("book", 12.32));
        items.Add(new Item("cd", 16.32));
        items.Add(new Item("bed", 124.2));
        items.Add(new Item("TV", 12.32));

        items.Sort();

        foreach (Item temp in items)
            Console.WriteLine("Name:{0} Price:{1}", temp.name, temp.price);
        Console.Read();            
    }


    class Item: IComparable
    {
        public string name;
        public double price;

        public Item(string _name, double _price)
        {
            this.name = _name;
            this.price = _price;
        }

        public int CompareTo(object obj)
        {   
            //note that I use the name property I may use a different one
            int temp = this.name.CompareTo(((Item)obj).name);
            return temp;
        }
    }
0
Tono Nam

Parce que les chaînes sont triées par ordre alphabétique et que le caractère de soulignement est après les caractères des nombres. Vous devez fournir un comparateur implémentant "Ordre naturel" pour obtenir le résultat souhaité. 

0
Matej

L'algorithme de comparaison de chaînes compare chaque caractère à la fois. 1 trie avant 2. Peu importe qu'il soit suivi d'un 1 ou d'un 2.

Donc, 100 serait trié avant 2. Si vous ne voulez pas ce comportement, vous avez besoin d'un algorithme de comparaison qui gère ce cas.

0
Zifre

Comme d'autres l'ont indiqué, les éléments seront triés par ordre alphabétique par défaut. La solution consiste à définir une classe Java.util.Comparator concrète et à la passer comme second argument à la méthode de tri. Votre comparateur devra analyser les entiers en tête des chaînes et les comparer.

0
jiggy