Ordinare gli oggetti in ArrayList per data?


149

Ogni esempio che trovo riguarda questa operazione in ordine alfabetico, mentre ho bisogno dei miei elementi ordinati per data.

My ArrayList contiene oggetti su cui uno dei gruppi di dati è un oggetto DateTime. In DateTime posso chiamare le funzioni:

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

Quindi per confrontare potrei fare qualcosa del tipo:

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

Cosa devo fare all'interno del blocco if?


3
Ho pubblicato la soluzione, ma se vuoi avere una visione d'ordine degli ordini, dovresti leggere gli algoritmi di ordinazione (bolle, fusione, porta, ecc.).
helios

Grazie, darò un'occhiata a quelli, non so nulla sull'ordinamento

Diverse soluzioni utili Java 1.8 potrebbe essere trovato in questa discussione: stackoverflow.com/questions/36361156/...
lradacher

Risposte:


418

Puoi rendere il tuo oggetto comparabile:

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

E poi lo ordini chiamando:

Collections.sort(myList);

Tuttavia, a volte non si desidera modificare il modello, ad esempio quando si desidera ordinare diverse proprietà. In tal caso, puoi creare un comparatore al volo:

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

Tuttavia, quanto sopra funziona solo se si è certi che dateTime non è null al momento del confronto. È consigliabile gestire anche null per evitare 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());
  }
}

O nel secondo esempio:

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
Buona risposta. Perché è necessario includere le versioni senza controllo null? Qual è il vantaggio? Se il modo corretto è il secondo, puoi solo includerlo e rendere più interessanti le informazioni importanti.
amotzg,

17
Due motivi: semplicità e rapidità. Vuoi che il codice sia il più semplice possibile, e se sei sicuro che la tua proprietà non dovrebbe essere nulla, probabilmente vuoi che il tuo codice fallisca il più presto possibile se incontri null, invece passa dati non validi e si interrompe ancora di più dal luogo dove sono stati introdotti dati non validi.
Domchi,

3
se o1 o o2 è nullo, restituisce 0; // questa riga potrebbe causare bug poiché la restituzione di 0 implica che sono uguali.
Tanyehzheng,

@tanyehzheng, è corretto, è fuorviante, ma nota che c'è una differenza tra .compareTo () che viene utilizzato per l'ordinamento e .equals (). Dipende davvero da come vuoi che i tuoi null vengano gestiti durante l'ordinamento.
Domchi,

1
È necessario controllare separatamente il valore di ogni data separatamente e ordinare come desiderato (ordinamenti nulli all'inizio o alla fine della sequenza). Cioè, se una data è nulla e una non è nulla, restituisce 1 o -1. Senza farlo, i valori null non vengono ordinati e rimangono ovunque si trovino nell'elenco prima dell'ordinamento.
droozen

64

Da Java 8 l' interfaccia Elenco fornisce il metodo di ordinamento . In combinazione con l' espressione lambda , la soluzione più semplice sarebbe

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

Ordinamento inverso

Java 8 include anche alcuni metodi utili per l'ordinamento inverso.

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

8
Ancora meglio sarebbelist.sort(Comparator.comparing(o -> o.getDateTime()));
Tunaki

21
Che ne dici di questo:list.sort(Comparator.comparing(MyObject::getDateTime)
sopracciglio

@Tunaki come fare l'ordinamento inverso?
giglio

18

È possibile utilizzare il metodo Collections.sort. È un metodo statico. Gli passi l'elenco e un comparatore. Utilizza un algoritmo di fusione modificato sull'elenco. Ecco perché è necessario passarlo a un comparatore per eseguire i confronti di coppia.

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

Nota che se myList è di tipo comparabile (uno che implementa l'interfaccia Comparable) (come Date, Integer o String) puoi omettere il comparatore e verrà usato l'ordinamento naturale.


1
E questo restituisce myList quando è finito o qualcosa del genere?

Modifica myList. Quindi è risolto quando finisce
helios


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

La migliore risposta IMHO da Tunaki utilizzando Java 8 lambda


8

Dato MyObjectche ha un DateTimemembro con un getDateTime()metodo, puoi ordinarne uno ArrayListche contiene MyObjectelementi in base agli DateTimeoggetti in questo modo:

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

E se volessi ordinare in base all'ora corrente del sistema.
Utente3

5

Ecco come ho risolto:

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

Spero che ti aiuti.


come invertirlo per data?
Zombie,

2

Con l'introduzione di Java 1.8, i flussi sono molto utili per risolvere questo tipo di problemi:

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

Tutte le risposte che ho trovato qui sono risultate inutilmente complesse per un semplice problema (almeno per uno sviluppatore Java esperto, che non lo sono). Ho avuto un problema simile e ho sperimentato questa (e altre) soluzioni, e sebbene fornissero un puntatore, per un principiante ho trovato come indicato sopra. La mia soluzione dipende da dove è nell'oggetto la tua data, in questo caso la data è il primo elemento dell'oggetto [] in cui dataVector è la matrice contenente gli oggetti.

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

6
In che modo la tua risposta è meno complicata o più corretta delle altre risposte qui? Secondo me, la risposta di WhiteFang32 ad esempio è molto simile e più concisa.
amotzg,

Sono stato via per un po ', quindi non ho visto la risposta, ma poi di nuovo, meglio tardi che mai. Penso che la concisione nella risposta di WhiteFang sia stata il suo svantaggio per i miei (allora) inesperti occhi da sviluppatore Java! L'inclusione del typecasting nella mia risposta è stata il copertoncino (almeno nella mia mente). Ciò che resta è dove hai dichiarato che la mia risposta era più corretta di altre? Scappando, suppongo .. tutto è perdonato!
Nepaluz,

O.getDateTime () non riguardava la digitazione, si tratta della descrizione dell'OP di un DateTimeoggetto contenuto in un altro oggetto. Se fosse solo Dateo DateTimeoggetti che non Comparablec'erano, non era necessario per Comparatorprima cosa.
amotzg

Non intendevo dire che la tua risposta è meno corretta o comunque meno buona. Ero solo io alla ricerca di informazioni per aiutarmi a comprenderle meglio. Mi dispiace se sembrasse diversamente.
amotzg

0

Questa potrebbe essere una vecchia risposta, ma ho usato alcuni esempi di questo post per creare un comparatore che ordinasse uno ArrayListdi HashMap<String, String>un oggetto nell'elenco, ovvero il timestamp.

Ho questi oggetti:

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

Gli oggetti della mappa sono i seguenti:

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");

Quella mappatura è ciò che uso per caricare tutti i miei oggetti nell'elenco di array, usando la alList.add(map)funzione, all'interno di un loop.

Ora, ho creato il mio comparatore:

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

Ora posso semplicemente chiamare il comparatore in qualsiasi momento sull'array e ordinerà il mio array, dandomi il timestamp più recente in posizione 0 (in cima all'elenco) e il timestamp più recente alla fine dell'elenco. I nuovi post vengono sostanzialmente messi in cima.

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

Questo può aiutare qualcuno, motivo per cui l'ho pubblicato. Prendi in considerazione le dichiarazioni di ritorno all'interno della funzione compare (). Esistono 3 tipi di risultati. Restituisce 0 se sono uguali, restituendo> 0 se la prima data è precedente alla seconda e restituendo <0 se la prima data è successiva alla seconda data. Se vuoi che la tua lista sia invertita, cambia semplicemente quelle due dichiarazioni di ritorno! Semplice =]


Volevo aggiungere un commento su questo. Naturalmente ci sono "NullPointerExceptions" quando si gestisce l'elenco di array (data l'istruzione return 0). Quindi dovrai gestirli poiché ogni caso sarà diverso. Ad esempio un elenco con 0 oggetti genererà una NullPointerException o un elenco con 1 oggetto!
Brandon,

0

Passa la ArrayList nell'argomento.

    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

Utilizzare l'approccio seguente per identificare le date sono ordinate o meno

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
Non sembra rispondere alla domanda. E per favore non insegnare ai giovani a usare la SimpleDateFormatclasse ormai obsoleta e notoriamente problematica . Almeno non come prima opzione. E non senza alcuna prenotazione. Oggi abbiamo molto meglio nella java.timemoderna API di data e ora di Java e nelle sue DateTimeFormatter.
Ole VV

0

La classe Date implementa già l'interfaccia di Comparator. Supponendo di avere la classe seguente:

public class A {

    private Date dateTime;

    public Date getDateTime() {
        return dateTime;
    }

    .... other variables

}

E supponiamo che tu abbia un elenco di oggetti A come List<A> aList, puoi facilmente ordinarlo con l'API dello stream di Java 8 (frammento di seguito):

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

...

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

-1

Futuri spettatori, penso che questa sia la soluzione più semplice, se il modello contiene una data di tipo stringa (ad esempio "2020-01-01 10:00:00"), quindi basta scrivere la riga seguente per ordinare i dati per data decrescente da dal più recente al più vecchio:

Collections.sort(messages, (o1, o2) -> o2.getMessageDate().compareTo(o1.getMessageDate()));
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.