Classificar objetos em ArrayList por data?

149

Cada exemplo que encontro é sobre fazer isso alfabeticamente, enquanto eu preciso dos meus elementos classificados por data.

My ArrayList contém objetos nos quais um dos membros de dados é um objeto DateTime. No DateTime, eu posso chamar as funções:

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

Então, para comparar, eu poderia fazer algo como:

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

O que devo fazer dentro do bloco if?

Trilarion
fonte
3
Postei a solução, mas se você quiser obter uma visão sobre o pedido, leia sobre algoritmos de pedido (bubblesort, mergesort, quicksort, etc.).
helios
Obrigado, vou dar uma olhada
Várias soluções Java 1.8 úteis podem ser encontradas neste segmento: stackoverflow.com/questions/36361156/…
lradacher

Respostas:

417

Você pode tornar seu objeto comparável:

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 então você ordena:

Collections.sort(myList);

No entanto, às vezes você não deseja alterar seu modelo, como quando deseja classificar em várias propriedades diferentes. Nesse caso, você pode criar um comparador rapidamente:

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

No entanto, o procedimento acima funciona apenas se você tiver certeza de que dateTime não é nulo no momento da comparação. É aconselhável manipular nulo, além de 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());
  }
}

Ou no segundo exemplo:

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
fonte
1
Boa resposta. Por que você precisa incluir as versões sem verificação nula? Qual a vantagem? Se o caminho correto for o segundo, você só pode incluí-lo e tornar as informações importantes mais atraentes.
amotzg
17
Duas razões - simplicidade e rapidez de falha. Você deseja que o código seja o mais simples possível e, se tiver certeza de que sua propriedade não deve ser nula, provavelmente desejará que seu código falhe o mais rápido possível, se você encontrar nulo, transmitindo dados inválidos e se afastando ainda mais do local onde dados inválidos foram introduzidos.
Domchi 10/09/12
3
se o1 ou o2 for nulo, retorne 0; // essa linha pode causar bug, pois retornar 0 implica que eles são iguais.
tanyehzheng
@tanyehzheng, isso é correto, é enganoso, mas observe que há uma diferença entre .compareTo () que é usada para classificação e .equals (). Realmente depende de como você deseja que seus nulos sejam tratados durante a classificação.
Domchi
1
Você deve verificar o nulo de cada data separadamente e classificar como desejado (classificações nulas no início ou no final da sequência). Ou seja, se uma data for nula e uma não for nula, retorne 1 ou -1. Sem fazer isso, os nulos não são classificados e permanecem onde quer que estejam na lista antes da classificação.
droozen
64

Desde o Java 8, a interface List fornece o método de classificação . Combinada com a expressão lambda, a solução mais fácil seria

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

Classificação inversa

O Java 8 também vem com alguns métodos úteis para classificação inversa.

//requested by lily
list.sort(Comparator.comparing(o -> o.getDateTime()).reversed());
Paul Wasilewski
fonte
8
Ainda melhor serialist.sort(Comparator.comparing(o -> o.getDateTime()));
Tunaki 18/16
21
Como sobre isso:list.sort(Comparator.comparing(MyObject::getDateTime)
whitebrow 10/04
@Tunaki como fazer a classificação reversa?
lily
18

Você pode usar o método Collections.sort. É um método estático. Você passa a lista e um comparador. Ele usa um algoritmo de fusão modificado sobre a lista. É por isso que você deve passar um comparador para fazer as comparações 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;
   }
});

Observe que se myList for de um tipo comparável (aquele que implementa interface comparável) (como Data, Inteiro ou String), você poderá omitir o comparador e a ordem natural será usada.

helios
fonte
1
E isso retorna myList quando está pronto ou algo assim?
Modifica myList. Então é ordenado quando termina
helios
@Sled: download.oracle.com/javase/6/docs/api/java/util/… , java.util.Comparator)
helios
9
list.sort(Comparator.comparing(o -> o.getDateTime()));

A melhor resposta IMHO de Tunaki usando Java 8 lambda

Stimpson Cat
fonte
8

Dado MyObjectque tem um DateTimemembro com um getDateTime()método, você pode classificar um ArrayListque contém MyObjectelementos pelos 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
fonte
E se eu quiser pedir com base na hora atual do sistema.
precisa
5

Foi assim que resolvi:

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

Espero que ajude você.

Igor Escobar
fonte
como reverter isso por data?
Zombie
2

Com a introdução do Java 1.8, os fluxos são muito úteis 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
fonte
1

Todas as respostas aqui foram consideradas desnecessariamente complexas para um problema simples (pelo menos para um desenvolvedor java experiente, o que não sou). Eu tive um problema semelhante e me deparei com essa (e outras) soluções e, embora elas fornecessem um ponteiro, para um iniciante que encontrei como mencionado acima. Minha solução depende de onde no objeto sua data é, nesse caso, a data é o primeiro elemento do objeto [] em que dataVector é o ArrayList que contém seus objetos.

Collections.sort(dataVector, new Comparator<Object[]>() {
    public int compare(Object[] o1, Object[] o2) {
        return ((Date)o1[0]).compareTo(((Date)o2[0]));
    }
});
Nepaluz
fonte
6
Como sua resposta é menos complicada ou mais correta do que outras respostas aqui? Na minha opinião, a resposta de WhiteFang32, por exemplo, é muito semelhante e mais concisa.
amotzg
Estive ausente por um tempo, então não vi a resposta, mas, novamente, antes tarde do que nunca. Eu acho que a concisão na resposta do WhiteFang foi sua desvantagem para os meus (então) inexperientes olhos de desenvolvedor java! A inclusão da tipografia na minha resposta foi o argumento decisivo (pelo menos na minha cabeça). O que resta é onde você obteve a alegação de que minha resposta estava mais correta do que outras? Desabafando, suponho .. tudo está perdoado!
Nepaluz 07/01
O o.getDateTime () não era sobre digitação, mas sobre a descrição do OP de um DateTimeobjeto que está contido dentro de outro objeto. Se fosse apenas Dateou DateTimeobjetos que são Comparablenão havia necessidade de um Comparatorem primeiro lugar.
amotzg
Não quis dizer que sua resposta é menos correta ou menos boa. Eu era apenas eu procurando informações para me ajudar a entender melhor. Me desculpe se parecia ser o contrário.
amotzg
0

Esta pode ser uma resposta antiga, mas usei alguns exemplos deste post para criar um comparador que classificaria um ArrayListde HashMap<String, String>um objeto na lista, sendo esse o carimbo de data / hora.

Eu tenho estes objetos:

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

Os objetos do mapa são os seguintes:

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

Esse mapeamento é o que eu uso para carregar todos os meus objetos na lista de matrizes, usando a alList.add(map)função, dentro de um loop.

Agora, eu criei meu próprio 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;
    }
}

Agora posso chamar o Comparador a qualquer momento na matriz e ela classificará minha matriz, fornecendo o carimbo de data / hora mais recente na posição 0 (topo da lista) e o carimbo de data / hora mais antigo no final da lista. Novas postagens são colocadas no topo basicamente.

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

Isso pode ajudar alguém, e é por isso que eu postei. Leve em consideração as instruções de retorno na função compare (). Existem 3 tipos de resultados. Retornando 0 se forem iguais, retornando> 0 se a primeira data for anterior à segunda data e retornando <0 se a primeira data for posterior à segunda data. Se você deseja que sua lista seja revertida, basta alternar essas duas instruções de retorno! Simples =]

Brandon
fonte
Eu queria adicionar um comentário sobre isso. Obviamente, existem "NullPointerExceptions" ao manipular a lista de matrizes (dada a instrução return 0). Então você terá que lidar com eles, pois cada caso será diferente. Por exemplo, uma lista com 0 objetos irá gerar uma NullPointerException ou uma lista com 1 objeto!
Brandon
0

Passe o 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
fonte
0

Use a abordagem abaixo para identificar as datas de classificação ou não

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
fonte
1
Parece não responder à pergunta. E, por favor, não ensine os jovens a usar a SimpleDateFormataula desatualizada e notoriamente problemática . Pelo menos não como a primeira opção. E não sem nenhuma reserva. Hoje, temos muito melhor java.time, a moderna API de data e hora de Java e suas DateTimeFormatter.
Ole VV
0

A classe Date já implementa a interface Comparator. Supondo que você tenha a classe abaixo:

public class A {

    private Date dateTime;

    public Date getDateTime() {
        return dateTime;
    }

    .... other variables

}

Digamos que você tenha uma lista de objetos A List<A> aList, você pode classificá-la facilmente com a API de fluxo do Java 8 (snippet abaixo):

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

...

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

Futuros espectadores, acho que essa é a solução mais simples, se o seu modelo contiver uma data do tipo string ("2020-01-01 10:00:00" por exemplo), então escreva a linha a seguir para classificar os dados por data descendente de mais recente para o mais antigo:

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