¿Ordenar objetos en ArrayList por fecha?

149

Cada ejemplo que encuentro trata de hacerlo alfabéticamente, mientras necesito mis elementos ordenados por fecha.

Mi ArrayList contiene objetos en los que uno de los miembros de datos es un objeto DateTime. En DateTime puedo llamar a las funciones:

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

Entonces, para comparar, podría hacer algo como:

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

¿Qué debo hacer dentro del bloque if?

Trilarion
fuente
3
Publiqué la solución, pero si desea obtener una idea sobre el pedido, debe leer sobre algoritmos de pedido (bubbleort, mergesort, quicksort, etc.).
helios
Gracias, voy a echar un vistazo a los que, no sé nada acerca de la clasificación
Se pueden encontrar varias soluciones Java 1.8 útiles en este hilo: stackoverflow.com/questions/36361156/…
lradacher el

Respuestas:

418

Puedes hacer que tu objeto sea 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());
  }
}

Y luego lo ordena llamando a:

Collections.sort(myList);

Sin embargo, a veces no desea cambiar su modelo, como cuando desea ordenar varias propiedades diferentes. En ese caso, puede crear un comparador sobre la marcha:

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

Sin embargo, lo anterior solo funciona si está seguro de que dateTime no es nulo en el momento de la comparación. Es aconsejable manejar nulo también para evitar 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 en el segundo ejemplo:

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());
  }
});
Domchi
fuente
1
Buena respuesta. ¿Por qué necesita incluir las versiones sin verificación nula? Cual es la ventaja? Si la forma correcta es la segunda, solo puede incluirla y hacer que la información importante sea más atractiva.
amotzg
17
Dos razones: simplicidad y falla rápida. Desea que el código sea lo más simple posible, y si está seguro de que su propiedad no debe ser nula, es probable que desee que su código falle lo antes posible si encuentra nulo, en lugar de pasar datos no válidos y romper aún más el lugar donde se introdujeron datos no válidos.
Domchi
3
si o1 u o2 es nulo, devuelve 0; // esta línea puede causar un error, ya que devolver 0 implica que son iguales.
tanyehzheng
@tanyehzheng, eso es correcto, es engañoso, pero tenga en cuenta que hay una diferencia entre .compareTo () que se usa para ordenar y .equals (). Realmente depende de cómo desea que se manejen sus valores nulos durante la ordenación.
Domchi
1
Debe verificar el nulo de cada fecha por separado y ordenarlo según lo desee (los ordenamientos nulos al principio o al final de la secuencia). Es decir, si una fecha es nula y la otra no, devuelva 1 o -1. Sin hacer eso, los valores nulos no se ordenan y permanecen dondequiera que estuvieran en la lista antes de la clasificación.
droozen
64

Desde Java 8, la interfaz de Lista proporciona el método de clasificación . En combinación con la expresión lambda , la solución más fácil sería

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

Clasificación inversa

Java 8 también viene con algunos métodos útiles para la clasificación inversa.

//requested by lily
list.sort(Comparator.comparing(o -> o.getDateTime()).reversed());
Paul Wasilewski
fuente
8
Aún mejor seríalist.sort(Comparator.comparing(o -> o.getDateTime()));
Tunaki
21
¿Qué tal esto:list.sort(Comparator.comparing(MyObject::getDateTime)
Whitebrow
@Tunaki ¿cómo hacer la clasificación inversa?
Lily
18

Puede usar el método Collections.sort. Es un método estático. Le pasas la lista y un comparador. Utiliza un algoritmo de mergesort modificado sobre la lista. Es por eso que debe pasarle un comparador para hacer las comparaciones de pares.

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

Tenga en cuenta que si myList es de un tipo comparable (uno que implementa una interfaz comparable) (como Date, Integer o String), puede omitir el comparador y se utilizará el orden natural.

helios
fuente
1
¿Y esto devuelve myList cuando está hecho o algo así?
Modifica myList. Entonces está ordenada cuando termina
helios
@Sled: download.oracle.com/javase/6/docs/api/java/util/… , java.util.Comparator)
helios
9
list.sort(Comparator.comparing(o -> o.getDateTime()));

La mejor respuesta en mi humilde opinión de Tunaki usando Java 8 lambda

Gato Stimpson
fuente
8

Dado MyObjectque tiene un DateTimemiembro con un getDateTime()método, puede ordenar uno ArrayListque contiene MyObjectelementos por los DateTimeobjetos como este:

Collections.sort(myList, new Comparator<MyObject>() {
    public int compare(MyObject o1, MyObject o2) {
        return o1.getDateTime().lt(o2.getDateTime()) ? -1 : 1;
    }
});
WhiteFang34
fuente
¿Qué pasa si quiero ordenar en base a la hora actual del sistema?
Usuario3
5

Así es como lo resolví:

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

Espero que te ayude.

Igor Escobar
fuente
¿Cómo revertirlo por fecha?
Zombi
2

Con la introducción de Java 1.8, las transmisiones son muy útiles para resolver este tipo de problemas:

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));
Tinki
fuente
1

Todas las respuestas aquí me parecieron innecesariamente complejas para un problema simple (al menos para un desarrollador Java experimentado, que yo no soy). Tuve un problema similar y encontré esta (y otras) soluciones, y aunque proporcionaron un puntero, para un principiante lo encontré como se indicó anteriormente. Mi solución depende de en qué parte del Objeto esté su Fecha, en este caso, la fecha es el primer elemento del Objeto [] donde dataVector es la ArrayList que contiene sus Objetos.

Collections.sort(dataVector, new Comparator<Object[]>() {
    public int compare(Object[] o1, Object[] o2) {
        return ((Date)o1[0]).compareTo(((Date)o2[0]));
    }
});
Nepalí
fuente
66
¿Cómo es su respuesta menos complicada o más correcta que otras respuestas aquí? En mi opinión, la respuesta de WhiteFang32, por ejemplo, es muy similar y más concisa.
amotzg
Estuve fuera por un tiempo, así que no vi la respuesta, pero de nuevo, más vale tarde que nunca. ¡Creo que la concisión en la respuesta de WhiteFang fue su inconveniente para mis (entonces) inexpertos ojos de desarrollador de Java! La inclusión de la tipificación en mi respuesta fue el factor decisivo (en mi mente, al menos, entonces). ¿Lo que queda es donde sacaste la afirmación de que mi respuesta fue más correcta que otras? Dejando de lado el vapor, supongo ... ¡todo está perdonado!
Nepaluz
El o.getDateTime () no se trataba de escribir, se trata de la descripción del OP de un DateTimeobjeto que está contenido dentro de otro objeto. Si era sólo Dateo DateTimelos objetos que se encuentran Comparableno había necesidad de una Comparatoren el primer lugar.
amotzg
No pretendía decir que su respuesta es menos correcta o menos buena. Solo estaba buscando información para ayudarme a comprenderla mejor. Lo siento si parecía ser de otra manera.
amotzg
0

Esto puede ser una respuesta de edad pero utilizado algunos ejemplos de este post para crear un comparador que ordenar un ArrayListde HashMap<String, String>por un objeto en la lista, que es la marca de tiempo.

Tengo estos objetos:

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

Los objetos del mapa son los siguientes:

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

Ese mapeo es lo que uso para cargar todos mis objetos en la lista de la matriz, usando la alList.add(map)función, dentro de un bucle.

Ahora, creé mi propio comparador:

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

Ahora puedo llamar al Comparador en cualquier momento en la matriz y ordenará mi matriz, dándome la última marca de tiempo en la posición 0 (parte superior de la lista) y la marca de tiempo más temprana al final de la lista. Las nuevas publicaciones se colocan en la parte superior básicamente.

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

Esto puede ayudar a alguien, por eso lo publiqué. Tenga en cuenta las declaraciones de devolución dentro de la función compare (). Hay 3 tipos de resultados. Devuelve 0 si son iguales, devuelve> 0 si la primera fecha es anterior a la segunda y devuelve <0 si la primera fecha es posterior a la segunda fecha. Si desea que su lista se invierta, ¡simplemente cambie esas dos declaraciones de devolución! Simple =]

Brandon
fuente
Quería agregar un comentario sobre esto. Por supuesto, hay "NullPointerExceptions" cuando se maneja la lista de matriz (dada la declaración return 0). Entonces tendrá que manejarlos, ya que cada caso será diferente. Por ejemplo, una lista con 0 objetos generará una NullPointerException, o una lista con 1 objeto.
Brandon
0

Pase el argumento 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);

        }
    });
}
Senghani Maulik
fuente
0

Utilice el siguiente enfoque para identificar las fechas ordenadas o no

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");
    }       
}   
Vikas Rathour
fuente
1
No parece responder la pregunta. Y por favor no les enseñe a los jóvenes a usar la SimpleDateFormatclase desactualizada y notoriamente problemática . Al menos no como la primera opción. Y no sin ninguna reserva. Hoy tenemos mucho mejor en java.timela moderna API de fecha y hora de Java y sus DateTimeFormatter.
Ole VV
0

La clase Date ya implementa la interfaz Comparator. Suponiendo que tiene la clase a continuación:

public class A {

    private Date dateTime;

    public Date getDateTime() {
        return dateTime;
    }

    .... other variables

}

Y supongamos que tiene una lista de objetos A, ya List<A> aListque puede ordenarlos fácilmente con la API de transmisión de Java 8 (fragmento a continuación):

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

...

aList = aList.stream()
        .sorted(Comparator.comparing(A::getDateTime))
        .collect(Collectors.toList())
Anthony Anyanwu
fuente
-1

Futuros espectadores, creo que esta es la solución más simple, si su modelo contiene una fecha de tipo de cadena ("2020-01-01 10:00:00" por ejemplo), simplemente escriba la siguiente línea para ordenar los datos por fecha descendente de lo más nuevo a lo más antiguo:

Collections.sort(messages, (o1, o2) -> o2.getMessageDate().compareTo(o1.getMessageDate()));
Hamza Al-Omari
fuente