Лучший способ классифицировать список - PullRequest
1 голос
/ 25 мая 2019

У меня есть объект заказа

public class Order{
  private Date orderDate;
  //other fields + getters & setters
}

И список заказов ( Список <Заказы> ). Теперь я хочу сгруппировать этот список заказов на основе аналогичной orderDate (в данном случае это означает «тот же год, месяц и день») и создать карту, подобную этой:

Map<Date, List<Order>> orderMap = new HashMap<>();

Как это сделать в Java 8+?

Обратите внимание, у нас может быть похожая дата, но другая временная метка, что означает, что мы хочу основать это на "год, месяц, день"

Ответы [ 5 ]

3 голосов
/ 25 мая 2019

Поскольку класс Date устарел и содержит действительные дату и время, вы можете использовать LocalDateTime в качестве поля в классе Order.Затем вы можете сгруппировать ваши заказы по LocalDate, взятым из LocalDateTime:

List<Order> list = new LinkedList<>();

Map<LocalDate, List<Order>> collect = list.stream()
                .collect(Collectors.groupingBy(order -> order.getOrderDate().toLocalDate()));

LocalDate содержит год, месяц и день без времени.

2 голосов
/ 25 мая 2019

tl; dr

Как ключ к вашему Map< LocalDate , List< Order> >:

    order
    .getDate()                                  // Accessor method to retrieve your `java.util.Date` object.
    .toInstant()                                // Convert from terrible legacy class `Date` to modern `java.time.Instant` class. Both represent a moment in UTC.
    .atZone(                                    // Adjust from UTC to the time zone used by your business.
            ZoneId.of( "America/Los_Angeles" ) 
    )                                           // Remember: For any given moment, the date varies around globe by time zone, “tomorrow” in Tokyo while “yesterday” in Montréal. 
    .toLocalDate()                              // Extract the date-only portion, without time-of-day, without time zone, to use as the key in our map.

Оставьте ваш Date член

Если вы не можете изменить тип вашегоorderDate переменная-член из ужасного класса java.util.Date, затем выполните бизнес-логику в java.time .Преобразуйте в / из Date, используя новые методы, добавленные к старым классам.

Делайте не используйте LocalDateTime

Используя java.time обсуждалось в других Ответах, но в них используется неправильный класс: LocalDateTime.Это совершенно неправильный класс для использования, поскольку не представляет момент.Он представляет потенциальных моментов в диапазоне около 26-27 часов, диапазоне часовых поясов по всему земному шару.Класс содержит дату и время дня, но не имеет понятия о часовом поясе или смещении от UTC.Поэтому, если объект LocalDateTime хранится в 12 часов вечера 23 января этого года, мы не знаем, должен ли это быть полдень в Токио, полдень в Калькутте, полдень в Париже или полдень в Монреале - все это очень разные моменты,несколько часов друг от друга.

Чтобы представить момент, используйте один из них:

  • InstantМомент в UTC
  • OffsetDateTimeМомент со смещением от UTC, некоторое количество часов-минут-секунд
  • ZonedDateTimeМомент, видимый через смещение, используемое людьми определенного региона, этот часовой пояс имеет имя в формате Continent/Region

Table of date-time types in Java, both modern and legacy

DateInstant

Замена java.util.Date - java.time.Instant.Оба представляют момент в UTC, хотя Instant имеет более точное разрешение наносекунд против миллисекунд.

Instant instant = orderDate.toInstant() ;

Часовой пояс

Настройте UTC на нужное времязона.Хотите ли вы воспринимать дату как Барстоу , Калифорния?Используйте America/Los_Angeles часовой пояс.Примените ZoneId, чтобы получить ZonedDateTime.Этот ZonedDateTime представляет тот же момент, ту же точку на временной шкале, что и Instant, но использует другое время для настенных часов.

Укажите правильное имя часового пояса вформат Continent/Region, например America/Montreal, Africa/Casablanca или Pacific/Auckland.Никогда не используйте 2-4 буквенные сокращения, такие как EST или IST, поскольку они не истинные часовые пояса, не стандартизированы и даже не уникальны (!).

Обратите внимание, что java.time классы поточно-ориентированные по своей конструкции, используя неизменных объектов .Таким образом, вы можете определить это ZoneId один раз и сохранить его для повторного использования, даже в потоках.

ZoneId z = ZoneId.of( "America/Los_Angeles" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;  // Same moment, different wall-clock-time.

LocalDate

Извлечение части, содержащей только дату, без времени суток и без часового пояса.

LocalDate ld = zdt.toLocalDate() ;  // The date as seen for this moment in Barstow, California.

Использование LocalDate как ключ к вашему Map< LocalDate , List< Order> >.

Измените Date участника на Instant

Как это сделать в Java 8+?

Лучше всего перенести ваш код изужасные устаревшие классы даты и времени для современных java.time классов.Sun, Oracle и сообщество JCP отказались от старых классов, когда они приняли JSR 310. Так что, если вы - они действительно такие плохие.

Как правило, лучше всего работать с бэкэндом, думать, хранить и обмениватьсязначения даты и времени в UTC.Применяйте часовой пояс только для (а) представления пользователю, (б) если это требуется бизнес-логикой.

public class Order{
  private Instant whenOrdered ;
  //other fields + getters & setters
}

Примените ту же логику настройки часового пояса, что и выше, чтобы получить LocalDateкак ключ к вашему Map< LocalDate , List< Order > >.

Отображение Instant для пользователя в любом часовом поясе, который он предпочитает.Пусть java.time автоматически локализует отображаемый текст.

Locale locale = new Locale( "fr" , "TN" ) ;      // French in Tunisia.
ZoneId userZone = ZoneId.of( "Africa/Tunis" ) ;  // Or ZoneId.systemDefault() to get the JVM’s current default time zone (appropriate to a client machine, but not a server machine). 
String outputForDisplay = 
    order
    .whenOrdered
    .atZone( userZone )                          // Adjust from UTC to time zone, going from `Instant` to `ZonedDateTime`. 
    .format(                                     // Generate text representing the value inside our `ZonedDateTime` object.
        DateTimeFormatter.ofLocalizedDateTime( FormatStyle.LONG )  // Let java.time automatically localize, rather than hard-code a formatting pattern.
    ) 
;

Если вам важна только дата, а не время суток, и вы нене заботясь о нечеткости определения даты в разных часовых поясах (вы работаете в небольшом местном предприятии), используйте LocalDate.

public class Order{
  private LocalDate dateOrdered ;
  //other fields + getters & setters
}

About java.time

Фреймворк java.time встроен в Java 8 и более поздние версии.Эти классы вытесняют проблемные старые устаревшие классы даты и времени, такие как java.util.Date, Calendar и & SimpleDateFormat.

TЧтобы узнать больше, см. Oracle Tutorial .И поиск переполнения стека для многих примеров и объяснений.Спецификация: JSR 310 .

Проект Joda-Time , теперь в режиме обслуживания , рекомендует перейти на java.time классы.

Вы можете обмениваться java.time объектами напрямую с вашей базой данных.Используйте драйвер JDBC , совместимый с JDBC 4.2 или более поздней версии.Нет необходимости в строках, нет необходимости в java.sql.* классах.

Где получить классы java.time?

  • Java SE 8 , Java SE 9 , Java SE 10, Java SE 11 и более поздних версий - часть стандартного Java API с связанной реализацией.
    • Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и JavaSE 7
    • Большинство функций java.time перенесено в Java 6 & 7 в ThreeTen-Backport .
  • Android
    • Более поздние версии пакетов Android для реализации java.time классы.
    • Для более ранних версий Android (<26) проект <a href="https://github.com/JakeWharton/ThreeTenABP" rel="nofollow noreferrer"> ThreeTenABP адаптируется ThreeTen-Backport (упомянуто выше).См. Как использовать ThreeTenABP… .

ThreeTen-Extra Проект расширяет java.time дополнительными классами.Этот проект является полигоном для возможных будущих дополнений к java.time.Вы можете найти здесь несколько полезных классов, таких как Interval, YearWeek, YearQuarter и more .

2 голосов
/ 25 мая 2019

Если вам нужно использовать Date, вы все равно можете преобразовать его в LocalDate, предоставив информацию о часовом поясе.

Map<LocalDate, List<Order>> collect =
    list.stream().collect(Collectors.groupingBy(order -> 
        order.getOrderDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate()));
2 голосов
/ 25 мая 2019

Вы можете создать метод, который возвращает «сходство» заказов, например ::10000

public class Order{
  private Date orderDate;
  //other fields + getters & setters
  public String getStringDate(){
     SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
     return dateFormat.format(this.orderDate);
  }
}

и группировка по нему:

Map<String, List<Order>> ordersWithSimilarOrderDate = orders.stream().collect(Collectors.groupingBy(Order::getStringDate));
1 голос
/ 25 мая 2019

Я действительно не поддерживаю смысл делать все с потоками, но вы можете сделать это жестким способом преобразования.

 List<Order> orders = ... your list ...
 Map<Date, List<Order>> orderMap = orders.stream().collect(Collectors.groupingBy(order->{
      //Converting from Date to Instant
      Instant dateInstant = order.getOrderDate().toInstant();
      //Converting Instant to LocalDateTime and setting it to the start of the day
      LocalDateTime dateTime = dateInstant.atZone(ZoneId.systemDefault()).toLocalDate().atStartOfDay();
      //Converting it back to an instant
      Instant startOfDayInstant = dateTime.atZone(ZoneId.systemDefault()).toInstant();
      //Returning the util date
      return Date.from(startOfDayInstant);
    }));

Это при том условии, что вам нужно дата использования.В противном случае я поддерживаю ответ от michalk

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...