web-dev-qa-db-fra.com

Trier ArrayList des objets personnalisés par propriété

J'ai lu des informations sur le tri de ArrayLists à l'aide d'un comparateur, mais dans tous les exemples, les utilisateurs ont utilisé compareTo qui, selon certaines recherches, est une méthode pour les chaînes.

Je souhaitais trier une ArrayList d'objets personnalisés en fonction de l'une de leurs propriétés: un objet Date (getStartDay()). Normalement, je les compare par item1.getStartDate().before(item2.getStartDate()) alors je me demandais si je pouvais écrire quelque chose comme:

public class CustomComparator {
    public boolean compare(Object object1, Object object2) {
        return object1.getStartDate().before(object2.getStartDate());
    }
}

public class RandomName {
    ...
    Collections.sort(Database.arrayList, new CustomComparator);
    ...
}
1020
Samuel

Puisque Date implémente Comparable , il a une méthode compareTo tout comme String.

Donc, votre coutume Comparator pourrait ressembler à ceci:

public class CustomComparator implements Comparator<MyObject> {
    @Override
    public int compare(MyObject o1, MyObject o2) {
        return o1.getStartDate().compareTo(o2.getStartDate());
    }
}

La méthode compare() doit renvoyer un int, vous ne pouvez donc pas renvoyer directement un boolean comme vous le souhaitiez de toute façon.

Votre code de tri serait à peu près comme vous l'avez écrit:

Collections.sort(Database.arrayList, new CustomComparator());

Si vous n'avez pas besoin de réutiliser votre comparateur, une façon légèrement plus courte d'écrire tout cela est de l'écrire en tant que classe anonyme en ligne:

Collections.sort(Database.arrayList, new Comparator<MyObject>() {
    @Override
    public int compare(MyObject o1, MyObject o2) {
        return o1.getStartDate().compareTo(o2.getStartDate());
    }
});

Depuis Java-8

Vous pouvez maintenant écrire le dernier exemple sous une forme plus courte en utilisant un expression lambda pour le Comparator:

Collections.sort(Database.arrayList, 
                        (o1, o2) -> o1.getStartDate().compareTo(o2.getStartDate()));

Et List a la méthode sort(Comparator) , vous pouvez donc la raccourcir davantage:

Database.arrayList.sort((o1, o2) -> o1.getStartDate().compareTo(o2.getStartDate()));

C'est un idiome si commun qu'il existe une méthode intégrée pour générer un Comparator pour une classe avec une clé Comparable:

Database.arrayList.sort(Comparator.comparing(MyObject::getStartDate));

Toutes ces formes sont équivalentes.

1396
Michael Myers

Les classes qui ont un ordre de tri naturel (un numéro de classe, par exemple) doivent implémenter l'interface Comparable, tandis que les classes qui n'ont pas d'ordre de tri naturel (un président de classe, par exemple) doivent être accompagnées d'un comparateur (ou d'un comparateur anonyme classe).

Deux exemples:

public class Number implements Comparable<Number> {
    private int value;

    public Number(int value) { this.value = value; }
    public int compareTo(Number anotherInstance) {
        return this.value - anotherInstance.value;
    }
}

public class Chair {
    private int weight;
    private int height;

    public Chair(int weight, int height) {
        this.weight = weight;
        this.height = height;
    }
    /* Omitting getters and setters */
}
class ChairWeightComparator implements Comparator<Chair> {
    public int compare(Chair chair1, Chair chair2) {
        return chair1.getWeight() - chair2.getWeight();
    }
}
class ChairHeightComparator implements Comparator<Chair> {
    public int compare(Chair chair1, Chair chair2) {
        return chair1.getHeight() - chair2.getHeight();
    }
}

Usage:

List<Number> numbers = new ArrayList<Number>();
...
Collections.sort(numbers);

List<Chair> chairs = new ArrayList<Chair>();
// Sort by weight:
Collections.sort(chairs, new ChairWeightComparator());
// Sort by height:
Collections.sort(chairs, new ChairHeightComparator());

// You can also create anonymous comparators;
// Sort by color:
Collections.sort(chairs, new Comparator<Chair>() {
    public int compare(Chair chair1, Chair chair2) {
        ...
    }
});
189
Björn

Pour trier une ArrayList, vous pouvez utiliser l'extrait de code suivant:

Collections.sort(studList, new Comparator<Student>(){
    public int compare(Student s1, Student s2) {
        return s1.getFirstName().compareToIgnoreCase(s2.getFirstName());
    }
});
153
user1377710

Oui, vous pouvez. Il existe deux options permettant de comparer des éléments, l’interface Comparable et l’interface Comparator .

Ces deux interfaces permettent un comportement différent. Comparable vous permet de faire agir l'objet comme vous venez de décrire Strings (en fait, String implémente Comparable). La seconde, Comparateur, vous permet de faire ce que vous demandez. Vous le feriez comme ceci:

Collections.sort(myArrayList, new MyComparator());

La méthode Collections.sort utilisera alors votre comparateur pour son mécanisme de tri. Si les objets de ArrayList implémentent de manière comparable, vous pouvez plutôt faire quelque chose comme ceci:

Collections.sort(myArrayList);

La classe Collections contient un certain nombre de ces outils utiles et communs.

41
aperkins

Expression lambda Java 8

Collections.sort(studList, (Student s1, Student s2) ->{
        return s1.getFirstName().compareToIgnoreCase(s2.getFirstName());
});

OR

Comparator<Student> c = (s1, s2) -> s1.firstName.compareTo(s2.firstName);
studList.sort(c)
36
Sorter

Avec Java 8, vous pouvez utiliser une référence de méthode pour votre comparateur:

import static Java.util.Comparator.comparing;

Collections.sort(list, comparing(MyObject::getStartDate));
29
assylias
import Java.text.ParseException;
import Java.text.SimpleDateFormat;
import Java.util.ArrayList;
import Java.util.Collections;
import Java.util.Comparator;
import Java.util.Date;

public class test {

public static class Person {
    public String name;
    public int id;
    public Date hireDate;

    public Person(String iname, int iid, Date ihireDate) {
        name = iname;
        id = iid;
        hireDate = ihireDate;
    }

    public String toString() {
        return name + " " + id + " " + hireDate.toString();
    }

    // Comparator
    public static class CompId implements Comparator<Person> {
        @Override
        public int compare(Person arg0, Person arg1) {
            return arg0.id - arg1.id;
        }
    }

    public static class CompDate implements Comparator<Person> {
        private int mod = 1;
        public CompDate(boolean desc) {
            if (desc) mod =-1;
        }
        @Override
        public int compare(Person arg0, Person arg1) {
            return mod*arg0.hireDate.compareTo(arg1.hireDate);
        }
    }
}

public static void main(String[] args) {
    // TODO Auto-generated method stub
    SimpleDateFormat df = new SimpleDateFormat("mm-dd-yyyy");
    ArrayList<Person> people;
    people = new ArrayList<Person>();
    try {
        people.add(new Person("Joe", 92422, df.parse("12-12-2010")));
        people.add(new Person("Joef", 24122, df.parse("1-12-2010")));
        people.add(new Person("Joee", 24922, df.parse("12-2-2010")));
    } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    Collections.sort(people, new Person.CompId());
    System.out.println("BY ID");
    for (Person p : people) {
        System.out.println(p.toString());
    }

    Collections.sort(people, new Person.CompDate(false));
    System.out.println("BY Date asc");
    for (Person p : people) {
        System.out.println(p.toString());
    }
    Collections.sort(people, new Person.CompDate(true));
    System.out.println("BY Date desc");
    for (Person p : people) {
        System.out.println(p.toString());
    }

}

}
13
CharlesW

Puisque les technologies apparaissent tous les jours, la réponse changera dans le temps. J'ai jeté un coup d'œil à LambdaJ et cela semble très intéressant.

Vous pouvez essayer de résoudre ces tâches avec LambdaJ. Vous pouvez le trouver ici: http://code.google.com/p/lambdaj/

Ici vous avez un exemple:

Trier itératif

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

Trier avec lambda

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

Bien sûr, avoir ce genre de beauté impacte la performance (en moyenne 2 fois), mais pouvez-vous trouver un code plus lisible?

13
Federico Piazza

Le meilleur moyen facile avec Java 8 est de trier par ordre alphabétique anglais

Mise en œuvre de la classe

public class NewspaperClass implements Comparable<NewspaperClass>{
   public String name;

   @Override
   public int compareTo(NewspaperClass another) {
      return name.compareTo(another.name);
   }
}

Trier

  Collections.sort(Your List);

Si vous souhaitez trier un alphabet contenant des caractères non anglais, vous pouvez utiliser les paramètres régionaux ... Sous le code, utilisez le type de caractère turc ...

Mise en œuvre de la classe

public class NewspaperClass implements Comparator<NewspaperClass> {
   public String name;
   public Boolean isUserNewspaper=false;
   private Collator trCollator = Collator.getInstance(new Locale("tr_TR"));



   @Override
   public int compare(NewspaperClass lhs, NewspaperClass rhs) {
      trCollator.setStrength(Collator.PRIMARY);
      return trCollator.compare(lhs.name,rhs.name);
   }
}

Trier

Collections.sort(your array list,new NewspaperClass());
9
Beyaz

À partir de Java 8 et plus, nous n’avons pas besoin d’utiliser directement Collections.sort(). L’interface List a une méthode sort() par défaut:

List<User> users = Arrays.asList(user1,user2,user3);
users.sort( (u1, u2) -> { 
return u1.getFirstName.compareTo(u2.getFirstName());}); 

Voir http://visvv.blogspot.in/2016/01/sorting-objects-in-Java-8.html .

6
Vishnu Vasudevan

Référence de fonction et de méthode

La méthode Collections.sort peut trier une List en utilisant un Comparator vous passez. Cette Comparator peut être implémentée en utilisant la méthode Comparator.comparing où vous pouvez passer une référence à la méthode en tant que nécessaire Function . Heureusement, le code actuel est beaucoup plus simple et plus court que cette description.

Pour Java 8:

Collections.sort(list, comparing(ClassName::getName));

ou

Collections.sort(list, comparing(ClassName::getName).reversed());

Une autre façon est

Collections.sort(list, comparing(ClassName::getName, Comparator.nullsLast(Comparator.naturalOrder())));
6
Arun

Java 8 Lambda raccourcit le tri.

Collections.sort(stdList, (o1, o2) -> o1.getName().compareTo(o2.getName()));
5
javasenior

Vous pouvez essayer Guava Ordering :

Function<Item, Date> getStartDate = new Function<Item, Date>() {
    public Date apply(Item item) {
        return item.getStartDate();
    }
};

List<Item> orderedItems = Ordering.natural().onResultOf(getStartDate).
                          sortedCopy(items);
5
Vitalii Fedorenko

Vous pouvez trier à l'aide de Java 8

yourList.sort(Comparator.comparing(Classname::getName));

or

yourList.stream().forEach(a -> a.getBObjects().sort(Comparator.comparing(Classname::getValue)));
5
Seeker

Vous pouvez utiliser le comparateur Bean pour trier n'importe quelle propriété de votre classe personnalisée.

5
camickr

Oui, c'est possible par exemple dans cette réponse je trie par la propriété v de la classe IndexValue 

    // Sorting by property v using a custom comparator.
    Arrays.sort( array, new Comparator<IndexValue>(){
        public int compare( IndexValue a, IndexValue b ){
            return a.v - b.v;
        }
    });

Si vous remarquez ici, je crée une classe intérieure anonymous (qui est le Java pour les fermetures) et le passe directement à la méthode sort de la classe Arrays 

Votre objet peut également implémenter Comparable (c'est ce que fait String et la plupart des bibliothèques principales en Java), mais cela définirait "l'ordre de tri naturel" de la classe qu'il a elle-même et ne permet pas d'en brancher de nouveaux.

5
OscarRyz

J'ai trouvé que la plupart des réponses, sinon toutes, reposent sur la classe sous-jacente (Object) pour mettre en œuvre une interface comparable ou pour créer une interface similaire d'aide.

Pas avec ma solution! Le code suivant vous permet de comparer le champ d'un objet en connaissant son nom de chaîne. Vous pouvez facilement le modifier pour ne pas utiliser le nom, mais vous devez ensuite l'exposer ou construire l'un des objets que vous souhaitez comparer.

Collections.sort(anArrayListOfSomeObjectPerhapsUsersOrSomething, new ReflectiveComparator(). new ListComparator("name"));

public class ReflectiveComparator {
    public class FieldComparator implements Comparator<Object> {
        private String fieldName;

        public FieldComparator(String fieldName){
            this.fieldName = fieldName;
        }

        @SuppressWarnings({ "unchecked", "rawtypes" })
        @Override
        public int compare(Object object1, Object object2) {
            try {
                Field field = object1.getClass().getDeclaredField(fieldName);
                field.setAccessible(true);

                Comparable object1FieldValue = (Comparable) field.get(object1);
                Comparable object2FieldValue = (Comparable) field.get(object2);

                return object1FieldValue.compareTo(object2FieldValue);
            }catch (Exception e){}

            return 0;
        }
    }

    public class ListComparator implements Comparator<Object> {
        private String fieldName;

        public ListComparator(String fieldName) {
            this.fieldName = fieldName;
        }

        @SuppressWarnings({ "unchecked", "rawtypes" })
        @Override
        public int compare(Object object1, Object object2) {
            try {
                Field field = object1.getClass().getDeclaredField(fieldName);
                field.setAccessible(true);
                Comparable o1FieldValue = (Comparable) field.get(object1);
                Comparable o2FieldValue = (Comparable) field.get(object2);

                if (o1FieldValue == null){ return -1;}
                if (o2FieldValue == null){ return 1;}
                return o1FieldValue.compareTo(o2FieldValue);
            } catch (NoSuchFieldException e) {
                throw new IllegalStateException("Field doesn't exist", e);
            } catch (IllegalAccessException e) {
                throw new IllegalStateException("Field inaccessible", e);
            }
        }
    }
}
4
Kevin Parker

Ces extraits de code pourraient être utiles. Si vous voulez trier un objet dans mon cas, je veux trier par NomVolume:

public List<Volume> getSortedVolumes() throws SystemException {
    List<Volume> volumes = VolumeLocalServiceUtil.getAllVolumes();
    Collections.sort(volumes, new Comparator<Volume>() {
        public int compare(Volume o1, Volume o2) {
            Volume p1 = (Volume) o1;
            Volume p2 = (Volume) o2;
            return p1.getVolumeName().compareToIgnoreCase(
                    p2.getVolumeName());
        }
    });
    return volumes;
}

Cela marche. Je l'utilise dans mes jsp.

3
Laura Liparulo

Vous pouvez jeter un coup d'œil à cette présentation tenue au Forum Java à Stuttgart en Allemagne en 2016.

Seules quelques diapositives utilisent la langue allemande, 99% du contenu est du code source Java "basé sur l'anglais"; comme

someCollection.sort(
  OurCustomComparator
    .comparing(Person::getName)
    .thenComparing(Person::getId)
);

OurCustomComparator utilise des méthodes par défaut (et d’autres idées intéressantes). Comme indiqué, cela conduit à un code très concis pour choisir une méthode de tri pour le getter; et l'enchaînement (ou l'inversion) super simple des critères de tri. 

Si vous aimez Java8, vous y trouverez beaucoup de matériel pour vous aider à démarrer.

3
GhostCat

votre classe customComparator doit implémenter Java.util.Comparator pour pouvoir être utilisée . elle doit également ignorer compare () AND equals ()

compare () doit répondre à la question: l'objet 1 est-il inférieur, égal ou supérieur à l'objet 2?

documents complets: http://Java.Sun.com/j2se/1.5.0/docs/api/Java/util/Comparator.html

2
Vinny

Avec cette bibliothèque ici vous pouvez trier la liste des objets personnalisés sur plusieurs colonnes. La bibliothèque utilise les fonctionnalités de la version 8.0. Échantillon est également disponible là-bas. Voici un exemple à faire

SortKeys sortKeys = new SortKeys();
sortKeys.addField("firstName")
            .addField("age", true); // This (true) will sort the age descending

// Other ways to specify a property to the sorter are
//      .addField("lastName", String.class);
//      .addField("dob", Date.class, true);

// Instantiate a ListSorter
ListSorter listSorter = new ListSorter();

// Pass the data to sort (listToSort) and the "by keys" to sort (sortKeys)
List sortedList = (List<Person>) listSorter.sortList(listToSort, sortKeys);
2
Shriram M.

Depuis 1.8, la méthode List.sort () est nouvelle au lieu d’utiliser Collection.sort () Vous devez donc appeler directement mylistcontainer.sort ()

Voici un extrait de code qui illustre la fonctionnalité List.sort ():

List<Fruit> fruits = new ArrayList<Fruit>();
fruits.add(new Fruit("Kiwi","green",40));
fruits.add(new Fruit("Banana","yellow",100));
fruits.add(new Fruit("Apple","mixed green,red",120));
fruits.add(new Fruit("Cherry","red",10));

// a) using an existing compareto() method
fruits.sort((Fruit f1,Fruit f2) -> f1.getFruitName().compareTo(f2.getFruitName()));
System.out.println("Using String.compareTo(): " + fruits);
//Using String.compareTo(): [Apple is: mixed green,red, Banana is: yellow, Cherry is: red, Kiwi is: green]

// b) Using a comparable class
fruits.sort((Fruit f1,Fruit f2) -> f1.compareTo(f2));  
System.out.println("Using a Comparable Fruit class (sort by color): " + fruits);
// Using a Comparable Fruit class (sort by color): [Kiwi is green, Apple is: mixed green,red, Cherry is: red, Banana is: yellow]

La classe des fruits est:

public class Fruit implements Comparable<Fruit>
{
    private String name;
    private String color;
    private int quantity;

    public Fruit(String name,String color,int quantity)
    { this.name = name; this.color = color; this.quantity = quantity; }

    public String getFruitName() { return name; }        
    public String getColor() { return color; }  
    public int getQuantity() { return quantity; }

    @Override public final int compareTo(Fruit f) // sorting the color
    {
        return this.color.compareTo(f.color);
    }     
    @Override public String toString()
    {   
        return (name + " is: " + color);
    }
} // end of Fruit class   
2
Norbert

Je préfère ce processus:

public class SortUtil
{    
    public static <T> List<T> sort(List<T> list, String sortByProperty)
    {
            Collections.sort(list, new BeanComparator(sortByProperty));
            return list;
    }
}

List<T> sortedList = SortUtil<T>.sort(unsortedList, "startDate");

Si votre liste d'objets a une propriété appelée startDate, vous appelez l'utiliser encore et encore. Vous pouvez même les chaîner startDate.time.

Cela nécessite que votre objet soit Comparable, ce qui signifie que vous avez besoin d'une implémentation compareTo, equals et hashCode.

Oui, cela pourrait être plus rapide ... Mais maintenant, vous n'avez plus besoin de créer un nouveau comparateur pour chaque type. Si vous pouvez économiser sur le temps de développement et abandonner le temps d’exécution, vous pouvez l’utiliser.

1
DDus

L'utilisation de Java 8 permet de définir Comparator sur une ligne à l'aide de Comparator.comparing()

Utilisez l'une des méthodes suivantes:

Option 1:

listToBeSorted.sort(Comparator.comparing(CustomObject::getStartDate));

Option 2:

Collections.sort(listToBeSorted, Comparator.comparing(CustomObject::getStartDate));
0
Sahil Chhabra

Votre classe personnalisée peut implémenter l'interface "Comparable", qui nécessite une implémentation de la méthode CompareTo. Dans la méthode CompareTo, vous pouvez ensuite définir ce que signifie qu'un objet est inférieur ou supérieur à l'autre objet. Donc, dans votre exemple, cela peut ressembler à quelque chose comme ça:

public class MyCustomClass implements Comparable<MyCustomClass>{

..........

 @Override
public int compareTo(MyCustomClass a) {
    if(this.getStartDate().before(a.getStartDate())){
        return -1;
    }else if(a.getStartDate().before(this.getStartDate())){
        return 1;
    }else {
        return 0;
    }
}

Un nombre négatif indique que this est plus petit que l'objet comparé. Un nombre positif indique que this est plus grand que l'objet comparé à un objet et un zéro signifie que les objets sont égaux.

Vous pouvez ensuite utiliser collections.sort (myList) pour trier votre liste sans avoir à introduire un comparateur. Cette méthode présente également l’avantage de trier automatiquement les objets si vous utilisez des structures de données de collection triées comme un TreeSet ou un TreeMap. 

Vous pouvez consulter cet article si vous souhaitez en savoir plus sur l'interface Comparable (divulgation: je suis l'auteur;)) https://nullbeans.com/the-Java-comparable-interface-automatic-sort -de-collections/

0
A.Ebrahim