Posortować obiekty w ArrayList według daty?

149

Każdy przykład, który znajduję, dotyczy robienia tego alfabetycznie, podczas gdy ja potrzebuję moich elementów posortowanych według daty.

My ArrayList zawiera obiekty, w których jeden z elementów datambers jest obiektem DateTime. W DateTime mogę wywołać funkcje:

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

Więc dla porównania mógłbym zrobić coś takiego:

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

Co powinienem zrobić wewnątrz bloku if?

Trilarion
źródło
3
Opublikowałem rozwiązanie, ale jeśli chcesz uzyskać wgląd w składanie zamówień, przeczytaj o algorytmach zamawiania (bubblesort, connectesort, quicksort itp.).
helios
Dzięki, przyjrzę się tym, nie wiem nic o sortowaniu
W tym wątku można znaleźć kilka przydatnych rozwiązań Java 1.8: stackoverflow.com/questions/36361156/ ...
lradacher

Odpowiedzi:

417

Możesz porównać swój obiekt:

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

A potem sortuj to, dzwoniąc:

Collections.sort(myList);

Czasami jednak nie chcesz zmieniać modelu, na przykład gdy chcesz posortować według kilku różnych właściwości. W takim przypadku możesz utworzyć komparator w locie:

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

Jednak powyższe działa tylko wtedy, gdy masz pewność, że dateTime nie jest null w momencie porównania. Dobrze jest również obsłużyć wartość null, aby uniknąć wyjątków 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());
  }
}

Lub w drugim przykładzie:

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
źródło
1
Dobra odpowiedź. Dlaczego musisz dołączyć wersje bez sprawdzania wartości null? Jaka jest zaleta? Jeśli właściwy sposób to drugi, możesz go tylko uwzględnić i uatrakcyjnić ważne informacje.
amotzg
17
Z dwóch powodów - prostota i niezawodność. Chcesz, aby kod był tak prosty, jak to tylko możliwe, a jeśli masz pewność, że Twoja właściwość nie powinna być zerowa, prawdopodobnie chcesz, aby kod zakończył się niepowodzeniem tak szybko, jak to możliwe, jeśli napotkasz wartość null, zamiast tego przekazywać nieprawidłowe dane i przerywać jeszcze dalej od miejsca gdzie wprowadzono nieprawidłowe dane.
Domchi,
3
jeśli o1 lub o2 ma wartość null, zwraca 0; // ta linia może spowodować błąd, ponieważ zwrócenie 0 oznacza, że ​​są równe.
tanyehzheng
@tanyehzheng, to prawda, jest to mylące, ale zwróć uwagę, że istnieje różnica między .compareTo (), który jest używany do sortowania, a .equals (). To naprawdę zależy od tego, jak chcesz, aby twoje wartości zerowe były obsługiwane podczas sortowania.
Domchi
1
Powinieneś osobno sprawdzić wartość null każdej daty i posortować według potrzeb (sortuje wartość null na początku lub na końcu sekwencji). Oznacza to, że jeśli jedna data jest pusta, a druga nie, zwraca 1 lub -1. Bez tego wartości null nie są sortowane i pozostają tam, gdzie znajdowały się na liście przed sortowaniem.
droozen
64

Od wersji Java 8 interfejs List udostępnia metodę sortowania . W połączeniu z wyrażeniem lambda najłatwiejszym rozwiązaniem byłoby

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

Odwrotne sortowanie

Java 8 zawiera również przydatne metody sortowania wstecznego.

//requested by lily
list.sort(Comparator.comparing(o -> o.getDateTime()).reversed());
Paul Wasilewski
źródło
8
Jeszcze lepiej byłobylist.sort(Comparator.comparing(o -> o.getDateTime()));
Tunaki
21
Co powiesz na to:list.sort(Comparator.comparing(MyObject::getDateTime)
whitebrow
@Tunaki jak zrobić odwrotne sortowanie?
lilia
18

Możesz użyć metody Collections.sort. To metoda statyczna. Przekazujesz mu listę i komparator. Używa zmodyfikowanego algorytmu scalania na liście. Dlatego musisz przekazać mu komparator, aby dokonać porównań par.

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

Zauważ, że jeśli myList jest porównywalnego typu (takiego, który implementuje interfejs Comparable) (jak Date, Integer lub String), możesz pominąć komparator i zostanie użyte naturalne uporządkowanie.

helios
źródło
1
I czy to zwraca moją listę po zakończeniu, czy coś w tym stylu?
Modyfikuje myList. Więc wszystko jest uporządkowane, kiedy się skończy
helios
@Sled: download.oracle.com/javase/6/docs/api/java/util/… , java.util.Comparator)
helios
9
list.sort(Comparator.comparing(o -> o.getDateTime()));

Najlepsza odpowiedź IMHO od Tunaki przy użyciu Java 8 lambda

Stimpson Cat
źródło
8

Biorąc pod uwagę, MyObjectże ma element DateTimeczłonkowski z getDateTime()metodą, możesz posortować plikArrayList zawierający MyObjectelementy według DateTimeobiektów takich jak ten:

Collections.sort(myList, new Comparator<MyObject>() {
    public int compare(MyObject o1, MyObject o2) {
        return o1.getDateTime().lt(o2.getDateTime()) ? -1 : 1;
    }
});
WhiteFang34
źródło
Co jeśli chcę zamówić na podstawie aktualnego czasu systemowego.
User3
5

Oto jak rozwiązałem:

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

Mam nadzieję, że ci to pomoże.

Igor Escobar
źródło
jak to odwrócić według daty?
Zombie
2

Wraz z wprowadzeniem Javy 1.8, strumienie są bardzo przydatne w rozwiązywaniu tego rodzaju problemów:

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
źródło
1

Wszystkie odpowiedzi tutaj okazały się niekoniecznie złożone w przypadku prostego problemu (przynajmniej dla doświadczonego programisty Java, którym nie jestem). Miałem podobny problem i trafiłem na to (i inne) rozwiązania i chociaż zapewniły one wskazówkę, dla początkującego znalazłem, jak wspomniano powyżej. Moje rozwiązanie zależy od tego, gdzie w obiekcie znajduje się Twoja data, w tym przypadku data jest pierwszym elementem obiektu [], gdzie dataVector to ArrayList zawierająca Twoje obiekty.

Collections.sort(dataVector, new Comparator<Object[]>() {
    public int compare(Object[] o1, Object[] o2) {
        return ((Date)o1[0]).compareTo(((Date)o2[0]));
    }
});
Nepaluz
źródło
6
Jak twoja odpowiedź jest mniej skomplikowana lub bardziej poprawna niż inne odpowiedzi tutaj? Moim zdaniem na przykład odpowiedź WhiteFang32 jest bardzo podobna i bardziej zwięzła.
amotzg
Nie było mnie przez jakiś czas, więc nie widziałem odpowiedzi, ale z drugiej strony, lepiej późno niż wcale. Myślę, że zwięzłość w odpowiedzi WhiteFang była jej wadą dla moich (wtedy) niedoświadczonych oczu programistów Java! Włączenie rzutowania do mojej odpowiedzi było klinczerem (przynajmniej w moim umyśle). To, co pozostało, jest tym, gdzie wyciągnąłeś twierdzenie, że moja odpowiedź była bardziej poprawna niż inne? Wydaje mi się, że wszystko jest wybaczone!
Nepaluz
Metoda o.getDateTime () nie dotyczyła wpisywania, chodzi o opis DateTimeobiektu, który znajduje się wewnątrz innego obiektu, przez OP . Gdyby to było tylko Datelub DateTimeprzedmioty, które są, to w Comparableogóle nie było potrzeby Comparator.
amotzg
Nie chciałem powiedzieć, że Twoja odpowiedź jest mniej poprawna lub w inny sposób mniej dobra. Po prostu szukałem informacji, które pomogłyby mi lepiej to zrozumieć. Przepraszam, jeśli wydawało się inaczej.
amotzg
0

Może to być stara odpowiedź, ale użyłem kilku przykładów z tego postu, aby utworzyć komparator, który posortowałby wartość ArrayListlub HashMap<String, String>według jednego obiektu na liście, czyli znacznika czasu.

Mam te obiekty:

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

Obiekty mapy są następujące:

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

To mapowanie jest tym, czego używam, aby załadować wszystkie moje obiekty do listy tablic, używając alList.add(map)funkcji, w pętli.

Teraz stworzyłem własny komparator:

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

Mogę teraz po prostu zadzwonić do Komparatora w dowolnym momencie w tablicy i posortuje moją tablicę, podając mi najnowszą sygnaturę czasową na pozycji 0 (u góry listy) i najwcześniejszy znacznik czasu na końcu listy. Zasadniczo nowe posty trafiają na szczyt.

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

Może to komuś pomóc, dlatego to opublikowałem. Weź pod uwagę instrukcje powrotu w funkcji compare (). Istnieją 3 rodzaje wyników. Zwracanie 0, jeśli są równe, zwracanie> 0, jeśli pierwsza data jest przed drugą datą i <0, jeśli pierwsza data jest późniejsza niż druga. Jeśli chcesz, aby Twoja lista została odwrócona, po prostu zamień te dwie instrukcje powrotu! Simple =]

Brandon
źródło
Chciałem dodać komentarz na ten temat. Oczywiście podczas obsługi listy tablic (biorąc pod uwagę instrukcję return 0) istnieją „NullPointerExceptions”. Więc będziesz musiał sobie z nimi poradzić, ponieważ każdy przypadek będzie inny. Na przykład lista z 0 obiektami wygeneruje wyjątek NullPointerException lub listę z 1 obiektem!
Brandon
0

Przekaż 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);

        }
    });
}
Senghani Maulik
źródło
0

Skorzystaj z poniższego podejścia, aby określić, czy daty są sortowane, czy nie

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
źródło
1
Nie wydaje się odpowiadać na pytanie. I proszę, nie uczcie młodych, aby korzystali z dawno przestarzałej i notorycznie kłopotliwej SimpleDateFormatklasy. Przynajmniej nie jako pierwsza opcja. I nie bez zastrzeżeń. Dziś mamy o wiele lepsze java.time, nowoczesne API daty i czasu Java i jego DateTimeFormatter.
Ole VV
0

Klasa Date implementuje już interfejs Comparator. Zakładając, że masz klasę poniżej:

public class A {

    private Date dateTime;

    public Date getDateTime() {
        return dateTime;
    }

    .... other variables

}

Powiedzmy, że masz listę obiektów A, ponieważ List<A> aListmożesz ją łatwo posortować za pomocą strumieniowego API Java 8 (fragment poniżej):

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

...

aList = aList.stream()
        .sorted(Comparator.comparing(A::getDateTime))
        .collect(Collectors.toList())
Anthony Anyanwu
źródło
-1

Przyszli widzowie, myślę, że jest to najprostsze rozwiązanie, jeśli twój model zawiera datę typu string (na przykład „2020-01-01 10:00:00”), po prostu napisz następujący wiersz, aby posortować dane według daty malejącej od od najnowszych do najstarszych:

Collections.sort(messages, (o1, o2) -> o2.getMessageDate().compareTo(o1.getMessageDate()));
Hamza Al-Omari
źródło