Сортувати об’єкти в ArrayList за датою?


149

Кожен знайдений мені приклад - це робити в алфавітному порядку, тоді як мені потрібні елементи, відсортовані за датою.

Мій ArrayList містить об'єкти, на яких одна з членів даних є об'єктом DateTime. У DateTime я можу викликати функції:

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

Тож для порівняння я міг би зробити щось на кшталт:

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

Що мені робити всередині блоку if?


3
Я розмістив рішення, але якщо ви хочете ознайомитись із замовленням, вам слід ознайомитися з алгоритмами замовлення (бульборт, mergesort, quicksort тощо).
геліос

Дякую, я перегляну ці, я нічого не знаю про сортування

Кілька корисних Java 1.8 рішення можна знайти в цій темі: stackoverflow.com/questions/36361156 / ...
lradacher

Відповіді:


417

Ви можете зробити об'єкт порівнянним:

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

А потім ви сортуєте це, зателефонувавши:

Collections.sort(myList);

Однак іноді ви не хочете змінювати свою модель, як, наприклад, коли потрібно сортувати декілька різних властивостей. У цьому випадку ви можете створити компаратор на льоту:

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

Однак вищезазначене працює лише в тому випадку, якщо ви впевнені, що dateTime не є нульовим на момент порівняння. Доцільно також обробляти null, щоб уникнути 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());
  }
}

Або у другому прикладі:

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
Хороша відповідь. Чому потрібно включати версії без нульової перевірки? Яка перевага? Якщо правильний шлях є другим, ви можете лише включити його та зробити важливу інформацію більш привабливою.
amotzg

17
Дві причини - простота і невдача. Ви хочете, щоб код був максимально простим, і якщо ви впевнені, що ваше майно не повинно бути недійсним, ви, мабуть, хочете, щоб ваш код вийшов з ладу якнайшвидше, якщо ви зіткнулися з нулем, замість цього передайте недійсні дані та ще більше відірвіться від місця де були введені недійсні дані
Домчі

3
якщо o1 або o2 є нульовим, поверніть 0; // Цей рядок може спричинити помилку, оскільки повернення 0 означає, що вони рівні.
tanyehzheng

@tanyehzheng, це правильно, це вводить в оману, але зауважте, що існує різниця між .compareTo (), який використовується для сортування, і .equals (). Це дійсно залежить від того, як ви хочете, щоб ваші нулі оброблялися під час сортування.
Домчі

1
Ви повинні перевірити нульову дату кожної дати окремо та сортувати за бажанням (будь-які нульові сортування на початку чи в кінці послідовності). Тобто, якщо одна дата є нульовою, а одна - недійсною, поверніть 1 або -1. Не роблячи цього, нулі не сортуються і залишаються там, де вони траплялися, у списку перед сортуванням.
droozen

64

Оскільки Java 8, інтерфейс List забезпечує метод сортування . У поєднанні з виразом лямбда було б найпростішим рішенням

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

Зворотне сортування

Java 8 також пропонує деякі зручні методи зворотного сортування.

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

8
Ще краще було бlist.sort(Comparator.comparing(o -> o.getDateTime()));
Тунакі

21
Як щодо цього:list.sort(Comparator.comparing(MyObject::getDateTime)
біла

@Tunaki як зробити зворотне сортування?
лілія

18

Можна використовувати метод Collections.sort. Це статичний метод. Ви передаєте йому список і компаратор. Він використовує модифікований алгоритм злиття над списком. Ось чому ви повинні пройти його компаратор, щоб зробити парне порівняння.

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

Зауважте, що якщо myList має порівнянний тип (той, який реалізує Порівняний інтерфейс) (наприклад, Дата, Цілий чи Строковий), ви можете опустити компаратор і буде використано природне впорядкування.


1
І чи повертає цей мій список, коли це зроблено чи щось подібне?

Він модифікує мій список. Тож її сортували, коли вона закінчується
геліос


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

Найкраща відповідь IMHO з Tunaki, використовуючи лямбду Java 8


8

Враховуючи, MyObjectщо має DateTimeчлен з getDateTime()методом, ви можете сортувати елемент, ArrayListякий містить MyObjectелементи за такими DateTimeоб'єктами:

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

Що робити, якщо я хочу замовити основу на поточний системний час.
Користувач3

5

Ось як я вирішив:

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

Сподіваюся, це допоможе вам.


як повернути його за датою?
Зомбі

2

Завдяки впровадженню Java 1.8, потоки дуже корисні для вирішення подібних проблем:

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

Усі відповіді тут я виявив не обов'язково складними для простої проблеми (принаймні для досвідченого розробника Java, яким я не є). У мене була аналогічна проблема, і я застосував цей (та інші) рішення, і хоча вони вказували на початківця, я знайшов початківця, як сказано вище. Моє рішення, залежить від того, де в Object ваша дата, в даному випадку дата є першим елементом Object [], де dataVector - це ArrayList, що містить ваші об'єкти.

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

6
Наскільки ваша відповідь менш складна або більш правильна, ніж інші відповіді тут? На мою думку, відповідь WhiteFang32, наприклад, дуже схожа і більш коротка.
amotzg

На деякий час віддалився, тому не побачив відгуку, але потім знову, краще пізно, ніж ніколи. Я думаю, що стислість у відповіді WhiteFang була недоліком моїх (тоді) недосвідчених очей розробника Java! Включення мовної передачі у мою відповідь було клинчером (на мою думку, принаймні, тоді). Залишилося, де ви вирвали твердження, що моя відповідь була правильнішою за інших? Я випускаю пар я гадаю .. все прощено!
Непалуз

O.getDateTime () полягав не в наборі тексту, а в описі ОП DateTimeоб'єкта, який міститься в іншому об'єкті. Якби це було тільки Dateабо DateTimeоб'єкти, які Comparableне було ніякої необхідності для Comparatorв першу чергу.
amotzg

Не мав на увазі сказати, що ваша відповідь менш правильна або інакше менш добра. Я просто шукав інформацію, щоб допомогти мені краще зрозуміти її. Перепрошую, якщо здавалося, що це інакше.
amotzg

0

Це може бути старий відповідь , але я використав кілька прикладів з цього поста , щоб створити компаратор , який буде сортувати ArrayListпо HashMap<String, String>одним об'єктом в списку, що є мітка часу.

У мене є ці об'єкти:

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

Об'єкти карти такі:

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

Це відображення - це те, що я використовую для завантаження всіх моїх об'єктів у список масивів, використовуючи alList.add(map)функцію, в циклі.

Тепер я створив власний компаратор:

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

Тепер я можу просто зателефонувати на компаратор у будь-який час на масив, і він буде сортувати мій масив, даючи мені останню часову позначку в позиції 0 (вгорі списку) та найдавнішу часову позначку в кінці списку. Нові повідомлення в основному ставлять на початок.

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

Це може допомогти комусь, тому я і розмістив це. Враховуйте твердження про повернення в межах функції порівняння (). Є 3 типи результатів. Повернення 0, якщо вони рівні, повернення> 0, якщо перша дата є перед другою датою, і повернення <0, якщо перша дата є після другої дати. Якщо ви хочете, щоб ваш список був перетворений, просто перемкніть ці два заяви про повернення! Простий =]


Я хотів додати коментар до цього. Звичайно, є "NullPointerExceptions" при обробці списку масивів (з огляду на оператор return 0). Тому вам доведеться поводитися з ними, оскільки кожен випадок буде різним. Наприклад, список з 0 об'єктами створить NullPointerException або список з 1 об'єктом!
Брендон

0

Передайте аргумент 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

Використовуйте підхід нижче, щоб визначити дати впорядковані чи ні

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
Схоже, це не відповідає на питання. І, будь ласка, не вчіть молодих людей користуватися давно застарілим та сумно відомим клопотом SimpleDateFormat. Принаймні, не як перший варіант. І не без жодного застереження. Сьогодні ми набагато краще java.timeвживаємо сучасний API дати та часу Java та його DateTimeFormatter.
Оле ВВ

0

Клас Дата вже реалізує інтерфейс компаратора. Припустимо, що у вас є клас нижче:

public class A {

    private Date dateTime;

    public Date getDateTime() {
        return dateTime;
    }

    .... other variables

}

Скажімо, у вас є список об'єктів A як List<A> aList, ви можете легко сортувати його за допомогою потокового API Java 8 (фрагмент нижче):

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

...

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

-1

Майбутні глядачі, я думаю, що це найпростіше рішення, якщо ваша модель містить дату рядка типу (наприклад, "2020-01-01 10:00:00"), тоді просто напишіть наступний рядок для сортування даних за датою, що починається від новітні до найстаріших:

Collections.sort(messages, (o1, o2) -> o2.getMessageDate().compareTo(o1.getMessageDate()));
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.