Trier les objets dans ArrayList par date?


149

Chaque exemple que je trouve consiste à faire cela par ordre alphabétique, alors que j'ai besoin de mes éléments triés par date.

My ArrayList contient des objets sur lesquels l'un des datamembers est un objet DateTime. Sur DateTime, je peux appeler les fonctions:

lt() // less-than
lteq() // less-than-or-equal-to

Donc, pour comparer, je pourrais faire quelque chose comme:

if(myList.get(i).lt(myList.get(j))){
    // ...
}

Que dois-je faire à l'intérieur du bloc if?


3
J'ai publié la solution, mais si vous voulez avoir un aperçu de la commande, vous devriez lire sur les algorithmes de commande (tri par bulles, tri par fusion, tri rapide, etc.).
helios

Merci, je vais jeter un oeil à ceux-ci, je ne sais rien sur le tri

Plusieurs solutions Java 1.8 utiles peuvent être trouvées dans ce fil: stackoverflow.com/questions/36361156
...

Réponses:


418

Vous pouvez rendre votre objet comparable:

public static class MyObject implements Comparable<MyObject> {

  private Date dateTime;

  public Date getDateTime() {
    return dateTime;
  }

  public void setDateTime(Date datetime) {
    this.dateTime = datetime;
  }

  @Override
  public int compareTo(MyObject o) {
    return getDateTime().compareTo(o.getDateTime());
  }
}

Et puis vous le triez en appelant:

Collections.sort(myList);

Cependant, parfois, vous ne souhaitez pas modifier votre modèle, comme lorsque vous souhaitez trier sur plusieurs propriétés différentes. Dans ce cas, vous pouvez créer un comparateur à la volée:

Collections.sort(myList, new Comparator<MyObject>() {
  public int compare(MyObject o1, MyObject o2) {
      return o1.getDateTime().compareTo(o2.getDateTime());
  }
});

Cependant, ce qui précède ne fonctionne que si vous êtes certain que dateTime n'est pas nul au moment de la comparaison. Il est sage de gérer également null pour éviter les NullPointerExceptions:

public static class MyObject implements Comparable<MyObject> {

  private Date dateTime;

  public Date getDateTime() {
    return dateTime;
  }

  public void setDateTime(Date datetime) {
    this.dateTime = datetime;
  }

  @Override
  public int compareTo(MyObject o) {
    if (getDateTime() == null || o.getDateTime() == null)
      return 0;
    return getDateTime().compareTo(o.getDateTime());
  }
}

Ou dans le deuxième exemple:

Collections.sort(myList, new Comparator<MyObject>() {
  public int compare(MyObject o1, MyObject o2) {
      if (o1.getDateTime() == null || o2.getDateTime() == null)
        return 0;
      return o1.getDateTime().compareTo(o2.getDateTime());
  }
});

1
Bonne réponse. Pourquoi avez-vous besoin d'inclure les versions sans vérification de null? Quel est l'avantage? Si le bon chemin est le second, vous ne pouvez que l'inclure et rendre les informations importantes plus attrayantes.
amotzg le

17
Deux raisons: simplicité et rapidité. Vous voulez que le code soit aussi simple que possible, et si vous êtes certain que votre propriété ne doit pas être nulle, vous voudrez probablement que votre code échoue le plus tôt possible si vous rencontrez null, au lieu de cela en passant des données invalides et en vous séparant encore plus de l'endroit où des données invalides ont été introduites.
Domchi

3
si o1 ou o2 est nul, renvoie 0; // cette ligne peut provoquer un bogue car le retour de 0 implique qu'ils sont égaux.
tanyehzheng

@tanyehzheng, c'est correct, c'est trompeur, mais notez qu'il y a une différence entre .compareTo () qui est utilisé pour le tri et .equals (). Cela dépend vraiment de la façon dont vous voulez que vos valeurs nulles soient gérées pendant le tri.
Domchi

1
Vous devez vérifier la valeur NULL de chaque date séparément et trier comme vous le souhaitez (soit la valeur NULL effectue un tri au début ou à la fin de la séquence). Autrement dit, si une date est nulle et une autre n'est pas nulle, retournez 1 ou -1. Sans cela, les valeurs nulles ne sont pas triées et restent là où elles se trouvaient dans la liste avant le tri.
droozen

64

Depuis Java 8, l' interface List fournit la méthode de tri . Combinée à l' expression lambda , la solution la plus simple serait

// sort DateTime typed list
list.sort((d1,d2) -> d1.compareTo(d2));
// or an object which has an DateTime attribute
list.sort((o1,o2) -> o1.getDateTime().compareTo(o2.getDateTime()));
// or like mentioned by Tunaki
list.sort(Comparator.comparing(o -> o.getDateTime()));

Tri inversé

Java 8 est également fourni avec des méthodes pratiques pour le tri inversé.

//requested by lily
list.sort(Comparator.comparing(o -> o.getDateTime()).reversed());

8
Encore mieux seraitlist.sort(Comparator.comparing(o -> o.getDateTime()));
Tunaki

21
Que diriez-vous de ceci:list.sort(Comparator.comparing(MyObject::getDateTime)
whitebrow

@Tunaki comment faire le tri inversé?
Lily le

18

Vous pouvez utiliser la méthode Collections.sort. C'est une méthode statique. Vous lui passez la liste et un comparateur. Il utilise un algorithme de tri de fusion modifié sur la liste. C'est pourquoi vous devez lui passer un comparateur pour faire les comparaisons de paires.

Collections.sort(myList, new Comparator<MyObject> {
   public int compare(MyObject o1, MyObject o2) {
      DateTime a = o1.getDateTime();
      DateTime b = o2.getDateTime();
      if (a.lt(b)) 
        return -1;
      else if (a.lteq(b)) // it's equals
         return 0;
      else
         return 1;
   }
});

Notez que si myList est d'un type comparable (celui qui implémente l'interface Comparable) (comme Date, Integer ou String), vous pouvez omettre le comparateur et l'ordre naturel sera utilisé.


1
Et cela renvoie-t-il myList quand c'est fait ou quelque chose comme ça?

Il modifie myList. Donc, c'est trié quand il se termine
helios


9
list.sort(Comparator.comparing(o -> o.getDateTime()));

La meilleure réponse à mon humble avis de Tunaki en utilisant Java 8 lambda


8

Étant donné MyObjectqu'il a un DateTimemembre avec une getDateTime()méthode, vous pouvez trier un ArrayListqui contient des MyObjectéléments par les DateTimeobjets comme ceci:

Collections.sort(myList, new Comparator<MyObject>() {
    public int compare(MyObject o1, MyObject o2) {
        return o1.getDateTime().lt(o2.getDateTime()) ? -1 : 1;
    }
});

Que faire si je souhaite commander en fonction de l'heure système actuelle.
User3

5

Voici comment j'ai résolu:

Collections.sort(MyList, (o1, o2) -> o1.getLastModified().compareTo(o2.getLastModified()));

J'espère que cela vous aidera.


comment l'inverser par date?
Zombie

2

Avec l'introduction de Java 1.8, les flux sont très utiles pour résoudre ce genre de problèmes:

Comparator <DateTime> myComparator = (arg1, arg2) 
                -> {
                    if(arg1.lt(arg2)) 
                       return -1;
                    else if (arg1.lteq(arg2))
                       return 0;
                    else
                       return 1;
                   };

ArrayList<DateTime> sortedList = myList
                   .stream()
                   .sorted(myComparator)
                   .collect(Collectors.toCollection(ArrayList::new));

1

Toutes les réponses que j'ai trouvées ici sont inutilement complexes pour un problème simple (du moins pour un développeur Java expérimenté, ce que je ne suis pas). J'ai eu un problème similaire et je suis tombé sur cette solution (et d'autres), et bien qu'elles aient fourni un pointeur, pour un débutant, j'ai trouvé comme indiqué ci-dessus. Ma solution dépend de l'emplacement de l'objet de votre date, dans ce cas, la date est le premier élément de l'objet [] où dataVector est le ArrayList contenant vos objets.

Collections.sort(dataVector, new Comparator<Object[]>() {
    public int compare(Object[] o1, Object[] o2) {
        return ((Date)o1[0]).compareTo(((Date)o2[0]));
    }
});

6
En quoi votre réponse est-elle moins compliquée ou plus correcte que les autres réponses ici? À mon avis, la réponse de WhiteFang32 par exemple est très similaire et plus concise.
amotzg le

J'étais absent pendant un certain temps, donc je n'ai pas vu la réponse, mais encore une fois, mieux vaut tard que jamais. Je pense que la concision de la réponse de WhiteFang était son inconvénient pour mes yeux de développeur Java (alors) inexpérimentés! L'inclusion du typage dans ma réponse a été le clincher (dans mon esprit alors du moins). Que reste-t-il, c'est là où vous avez fait valoir que ma réponse était plus correcte que d'autres? Se défouler je suppose .. tout est pardonné!
Nepaluz

Le o.getDateTime () n'était pas une question de frappe, il s'agit de la description par l'OP d'un DateTimeobjet contenu dans un autre objet. Si c'était seulement Dateou des DateTimeobjets, Comparableil n'y avait pas besoin d'un Comparator.
amotzg

Je ne voulais pas dire que votre réponse est moins correcte ou autrement moins bonne. J'étais juste à la recherche d'informations pour m'aider à mieux les comprendre. Je suis désolé s'il semblait en être autrement.
amotzg

0

Cela peut être une ancienne réponse mais j'ai utilisé quelques exemples de ce poste pour créer un comparateur qui trierait un ArrayListde HashMap<String, String>par un objet dans la liste, qui étant l'horodatage.

J'ai ces objets:

ArrayList<Map<String, String>> alList = new ArrayList<Map<String, String>>();

Les objets cartographiques sont les suivants:

Map<String, Object> map = new HashMap<>();
        // of course this is the actual formatted date below in the timestamp
        map.put("timestamp", "MM/dd/yyyy HH:mm:ss"); 
        map.put("item1", "my text goes here");
        map.put("item2", "my text goes here");

Ce mappage est ce que j'utilise pour charger tous mes objets dans la liste de tableaux, en utilisant la alList.add(map)fonction, dans une boucle.

Maintenant, j'ai créé mon propre comparateur:

import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;

 public class DateSorter implements Comparator {
     public int compare(Object firstObjToCompare, Object secondObjToCompare) {
    String firstDateString = ((HashMap<String, String>) firstObjToCompare).get("timestamp");
    String secondDateString = ((HashMap<String, String>) secondObjToCompare).get("timestamp");

    if (secondDateString == null || firstDateString == null) {
        return 0;
    }

    // Convert to Dates
    DateTimeFormatter dtf = DateTimeFormat.forPattern("MM/dd/yyyy HH:mm:ss");
    DateTime firstDate = dtf.parseDateTime(firstDateString);
    DateTime secondDate = dtf.parseDateTime(secondDateString);

    if (firstDate.isAfter(secondDate)) return -1;
    else if (firstDate.isBefore(secondDate)) return 1;
    else return 0;
    }
}

Je peux maintenant simplement appeler le comparateur à tout moment sur le tableau et il triera mon tableau, me donnant le dernier horodatage en position 0 (en haut de la liste) et le premier horodatage à la fin de la liste. Les nouveaux messages sont mis au sommet essentiellement.

Collections.sort(alList, new DateSorter());

Cela peut aider quelqu'un, c'est pourquoi je l'ai publié. Prenez en considération les instructions de retour dans la fonction compare (). Il existe 3 types de résultats. Renvoie 0 s'ils sont égaux, renvoie> 0 si la première date est antérieure à la deuxième date et renvoie <0 si la première date est postérieure à la deuxième. Si vous voulez que votre liste soit inversée, changez simplement ces deux instructions de retour! Simple =]


Je voulais ajouter un commentaire à ce sujet. Bien sûr, il existe des "NullPointerExceptions" lors de la gestion de la liste de tableaux (étant donné l'instruction return 0). Vous devrez donc les gérer car chaque cas sera différent. Par exemple, une liste avec 0 objet générera une NullPointerException, ou une liste avec 1 objet!
Brandon

0

Passez l'argument ArrayList In.

    private static void order(ArrayList<Object> list) {

    Collections.sort(list, new Comparator() {

        public int compare(Object o2, Object o1) {

            String x1 =  o1.Date;
            String x2 =  o2.Date;

                return  x1.compareTo(x2);

        }
    });
}

0

Utilisez l'approche ci-dessous pour identifier les dates triées ou non

SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-MM-yyyy");

boolean  decendingOrder = true;
    for(int index=0;index<date.size() - 1; index++) {
        if(simpleDateFormat.parse(date.get(index)).getTime() < simpleDateFormat.parse(date.get(index+1)).getTime()) {
            decendingOrder = false;
            break;
        }
    }
    if(decendingOrder) {
        System.out.println("Date are in Decending Order");
    }else {
        System.out.println("Date not in Decending Order");
    }       
}   

1
Cela ne semble pas répondre à la question. Et s'il vous plaît, n'apprenez pas aux jeunes à utiliser la SimpleDateFormatclasse depuis longtemps obsolète et notoirement gênante . Du moins pas comme première option. Et pas sans aucune réserve. Aujourd'hui, nous avons tellement mieux dans java.timel'API de date et d'heure Java moderne et ses DateTimeFormatter.
Ole VV

0

La classe Date implémente déjà l'interface Comparator. En supposant que vous ayez la classe ci-dessous:

public class A {

    private Date dateTime;

    public Date getDateTime() {
        return dateTime;
    }

    .... other variables

}

Et disons que vous avez une liste d'objets A List<A> aList, vous pouvez facilement la trier avec l'API de flux de Java 8 (extrait ci-dessous):

import java.util.Comparator;
import java.util.stream.Collectors;

...

aList = aList.stream()
        .sorted(Comparator.comparing(A::getDateTime))
        .collect(Collectors.toList())

-1

Futurs téléspectateurs, je pense que c'est la solution la plus simple, si votre modèle contient une chaîne de type date ("2020-01-01 10:00:00" par exemple), alors écrivez simplement la ligne suivante pour trier les données par date décroissant de du plus récent au plus ancien:

Collections.sort(messages, (o1, o2) -> o2.getMessageDate().compareTo(o1.getMessageDate()));
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.