【问题标题】:How to sort a list of months with years如何对带有年份的月份列表进行排序
【发布时间】:2015-05-20 07:33:06
【问题描述】:

我有一个带有年份的月份列表,例如:[12-2014,11-2012,5-2014,8-2012],我必须对它们进行排序最近的在顶部(或最新的日期在顶部) 例如。 [12-2014,5-2014,11-2012,8-2012]
有人知道如何在Java 中有效地做到这一点吗?

编辑:
YearMonth 的课程不可用,我在 Java 7

【问题讨论】:

  • 嗯,听起来你应该有一个YearMonth 类来保留年份和月份,并使其实现Comparable<YearMonth>...
  • 编写代码将是一个好的开始。如果这些是字符串,则只需在- 上拆分字符串,将部分转换为整数,然后对这些整数进行排序。
  • 如果你使用YearMonth,你可以使用date1.isBefore(date2)date1.isAfter(date2)之类的东西
  • @JonSkeet 更好的是,YearMonth 已经实现了 Comparabledocs.oracle.com/javase/8/docs/api/index.html 所以只需使用 Collections.sort 或 Stream.sorted,也许还有 Collections.reverseOrder。
  • 虽然 YearMonth 确实是与现代 Java 日期和时间 API java.time 一起引入的,但在 Java 8 中,java.time 也已向后移植到 Java 6 和 7。使用 ThreeTen Backport图书馆。

标签: java arrays date datetime time


【解决方案1】:

Java-8 以上

从列表中创建一个Stream,使用DateTimeFormatterStream 的每个String 映射到YearMonth,使用Comparator#reverseOrderStream 进行排序,映射每个YearMonth 元素使用相同的DateTimeFormatter 将流导入String,最后将Stream 收集到List<String>

演示:

import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;

public class Main {
    public static void main(String args[]) {
        List<String> list = List.of("12-2014", "11-2012", "5-2014", "8-2012");
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("M-uuuu", Locale.ENGLISH);
        List<String> sorted = 
                list.stream()
                    .map(s -> YearMonth.parse(s, dtf))
                    .sorted(Comparator.reverseOrder())
                    .map(ym -> dtf.format(ym))
                    .collect(Collectors.toList());

        // Display the list
        System.out.println(sorted);
    }
}

输出:

[12-2014, 5-2014, 11-2012, 8-2012]

ONLINE DEMO

通过 Trail: Date Time 了解有关 modern Date-Time API* 的更多信息。


* 如果您正在为一个 Android 项目工作,并且您的 Android API 级别仍然不符合 Java-8,请检查 Java 8+ APIs available through desugaring。请注意,Android 8.0 Oreo 已经提供了support for java.time

【讨论】:

    【解决方案2】:

    由于您使用的是 Java 7,因此您可以利用 Date 类以及 Comparator 接口及其在 Set 对象(例如 treeSet)中的用法。

    这是比较两个日期的 Comparator 接口的实现

    public class MonthYearComparator implements Comparator<Date> {
    
        public int compare(Date o1, Date o2) {
            // TODO Auto-generated method stub
            return -1*o1.compareTo(o2);
        }
    
    }
    

    接下来是我们如何使用它。首先,我将使用 SimpleDateFormat 类将您的字符串解析为日期。为此,我需要完成它们并使它们看起来像格式化的日期字符串。由于这一天与比较无关,我可以选择像“01”这样的任何一天。

    将它们转换为 Date 对象后,我会将它们添加到 TreeSet 的实例中,该实例提供有 Comparator,并且在我添加它们时它们将自动排序。

    然后我可以减去我需要的日期部分,这将是我的日期对象在格式化为字符串后的子字符串(3)。

    这是代码(为了演示,我使用了您提供的相同数据作为示例):

    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    import java.util.Set;
    import java.util.TreeSet;
    
    public class MonthYearIssue {
    
        private List<String> listOfMonthYears = new ArrayList<String>();
        private static final String USED_DATE_FORMAT = "dd-MM-yyyy";
        SimpleDateFormat formatter = new SimpleDateFormat(USED_DATE_FORMAT);
    
        public void setUpMonthYearsList() {
            listOfMonthYears.add("12-2014");
            listOfMonthYears.add("11-2012");
            listOfMonthYears.add("5-2014");
            listOfMonthYears.add("8-2012");
        }
    
        public Date parseToDate(String monthYearString) throws ParseException {
            return formatter.parse("01-" + monthYearString);
        }
    
        public List<String> doSort() throws ParseException {
    
            List<String> result = new ArrayList<String>();
            Set<Date> dates = new TreeSet<Date>(new MonthYearComparator());
    
            for (String monthYearStr: listOfMonthYears) {
                dates.add(parseToDate(monthYearStr));
    
            }
    
            for (Object d: dates.toArray()) {
    
                result.add(formatter.format((Date)d).substring(3));
            }
    
            System.out.println(result);
            return result;
    
        }
    
        public static void main(String[] args) throws ParseException {
            MonthYearIssue issueSolver = new MonthYearIssue();
            issueSolver.setUpMonthYearsList();
            issueSolver.doSort();
        }
    
    }
    

    结果如下:

    [12-2014, 05-2014, 11-2012, 08-2012]

    【讨论】:

    • 虽然该解决方案适用于给定的输入,但代码存在几个问题: 1. 使用 Set 会导致丢失重复条目。如果要对列表进行排序,请使用 Collections.sort。 2.你不必写一个逆比较器,JDK已经提供了一个通用的:Collections.reverseOrder()。 3. 为什么要加上“01-”,格式化“dd-”,然后调用subset?我认为简单地使用 MM-yyyy 应该可以工作(我还没有测试默认日期是什么,但我认为这不重要,也不应该引发错误)。
    • @Puce 感谢您的 cmets。我想使用 MM-yyyy 但不知道哪一天,我拿起了一个,因为它无关紧要。
    • 请不要教年轻人使用过时且臭名昭著的SimpleDateFormat类。至少不是第一选择。而且不是没有任何保留。我们在java.time, the modern Java date and time API, 和它的DateTimeFormatter 中做得更好。
    【解决方案3】:

    使用自定义字符串比较器。 http://docs.oracle.com/javase/7/docs/api/java/util/Comparator.html

    如果你愿意,我可以把代码写出来。

    这里是一些 sudo 代码:

    Arrays.sort(yearList, new Comparator<String>{
    
        //get the year and month of o1 and o2 as ints
    
        //if the years are different then return the difference of the years
    
        //if the years are the same then return the difference of the months
    
    }
    

    【讨论】:

      【解决方案4】:

      尝试类似(现已测试):

      private final static DateTimeFormatter YEAR_MONTH_FORMATTER = 
          DateTimeFormatter.ofPattern("M-yyyy");
      
      ...
      
      List<String> yearMonthStrings = ...;
      List<YearMonth> yearMonthList = yearMonthStrings.stream()
          .map(s -> YearMonth.parse(s, YEAR_MONTH_FORMATTER))
          .sorted()
          .collect(Collectors.toList());
      
      // or
      List<YearMonth> yearMonthList = yearMonthStrings.stream()
          .map(s -> YearMonth.parse(s, YEAR_MONTH_FORMATTER))
          .sorted(Collections.reverseOrder())
          .collect(Collectors.toList());
      

      后者给出[2014-12, 2014-05, 2012-11, 2012-08]

      【讨论】:

      • 类 YearMonth 不可用,我在 Java 7 上
      • 啊,这是当时问题中的编辑。请注意,Oracle 的 Java SE 7 计划在今年春天停产,所以如果您使用 Oracle JDK 并且没有与 Orcale 的支持合同,强烈建议升级到 Java SE 8。
      • 如果有人还在使用 Java 7(AKA 1.7)(2021 年不会很多),带有 YearMonthDateTimeFormatter 的 java.time 已被反向移植。使用ThreeTen Backport 库。
      【解决方案5】:

      尝试使用比较器或可比较器并通过拆分它们来检查月份。

      【讨论】:

        【解决方案6】:

        Date 类具有可比性,因此您可以为数组中的每个 String 创建一个 Date 实例,然后对它们进行排序

        【讨论】:

          【解决方案7】:

          tl;博士

          永远不要使用糟糕的旧日期时间类,例如 DateCalendar

          始终使用它们的替代品,即 JSR 310 中定义的现代 java.time 类。大部分功能已在 ThreeTen-Backport中向后移植到 Java 6 和 7 >.

          List < String > inputs = Arrays.asList( "12-2014" , "11-2012" , "5-2014" , "8-2012" );
          DateTimeFormatter f = DateTimeFormatter.ofPattern( "M-uuuu" );
          
          List < YearMonth > yearMonths = new ArrayList <>( inputs.size() );
          for ( String input : inputs )
          {
              try { yearMonths.add( YearMonth.parse( input , f ) ); } catch ( DateTimeException e ) { System.out.println( "ERROR - Faulty input: " + input ); }
          }
          
          Collections.sort( yearMonths , Collections.reverseOrder() );
          

          java.time 向后移植到 Java 7

          您说您仅限于 Java 7。虽然不是内置的,但您可以使用大多数现代 java.time 功能。将ThreeTen-Backport 库添加到您的项目中。

          如果使用 Maven,请将其添加到您的 POM:

          <dependency>
            <groupId>org.threeten</groupId>
            <artifactId>threetenbp</artifactId>
            <version>1.5.1</version>
          </dependency>
          

          DateCalendarSimpleDateFormat 等传统日期时间类在设计上存在严重缺陷。它们真的是一团糟,以至于在你的项目中添加一个库是非常值得的。

          DateTimeFormatter

          设置您的输入。

          顺便说一句,您的输入文本使用的是自定义格式 M-YYYY。我建议您对数据的发布者进行有关ISO 8601 标准的教育,以格式化以文本方式交换的日期时间值。年月的标准格式是 YYYY-MM。

          定义格式化程序以匹配您的非标准输入。

          List < String > inputs = Arrays.asList( "12-2014" , "11-2012" , "5-2014" , "8-2012" );
          DateTimeFormatter f = DateTimeFormatter.ofPattern( "M-uuuu" );
          

          YearMonth

          解析您的输入 YearMonth 对象,为每个对象添加一个新的 List

          如果输入可能有问题,您可以捕获DateTimeException 来检测和处理此类问题。

          List < YearMonth > yearMonths = new ArrayList <>( inputs.size() );
          for ( String input : inputs )
          {
              try { yearMonths.add( YearMonth.parse( input , f ) ); } catch ( DateTimeException e ) { System.out.println( "ERROR - Faulty input: " + input ); }
          }
          

          Collections.sort && Collections.reverseOrder

          对您的列表进行排序。您想要最新的第一个,因此传递可选的第二个参数 Comparator 以反转自然顺序的排序。

          Collections.sort( yearMonths , Collections.reverseOrder() );
          

          报告我们的新名单。

          System.out.println( "yearMonths = " + yearMonths );
          

          示例代码

          将所有代码放在一起。

          package work.basil;
          
          import org.threeten.bp.DateTimeException;
          import org.threeten.bp.YearMonth;
          import org.threeten.bp.format.DateTimeFormatter;
          
          import java.util.ArrayList;
          import java.util.Arrays;
          import java.util.Collections;
          import java.util.List;
          
          /**
           * Example parsing year-month values using ThreeTen-Backport library
           * giving us most of the Java 8+ *java.time* functionality.
           **/
          public class App
          {
              public static void main ( String[] args )
              {
                  List < String > inputs = Arrays.asList( "12-2014" , "11-2012" , "5-2014" , "8-2012" );
                  DateTimeFormatter f = DateTimeFormatter.ofPattern( "M-uuuu" );
          
                  List < YearMonth > yearMonths = new ArrayList <>( inputs.size() );
                  for ( String input : inputs )
                  {
                      try { yearMonths.add( YearMonth.parse( input , f ) ); } catch ( DateTimeException e ) { System.out.println( "ERROR - Faulty input: " + input ); }
                  }
          
                  Collections.sort( yearMonths , Collections.reverseOrder() );
          
                  System.out.println( "yearMonths = " + yearMonths );
              }
          }
          

          运行时。

          年月 = [2014-12, 2014-05, 2012-11, 2012-08]


          顺便说一下,这里是这个应用程序的一个 Maven POM,使用了各个部分的最新版本。

          <?xml version="1.0" encoding="UTF-8"?>
          
          <project xmlns = "http://maven.apache.org/POM/4.0.0"
                   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
              <modelVersion>4.0.0</modelVersion>
          
              <groupId>work.basil</groupId>
              <artifactId>J7</artifactId>
              <version>1.0-SNAPSHOT</version>
          
              <name>J7</name>
              <!-- FIXME change it to the project's website -->
              <url>http://www.example.com</url>
          
              <properties>
                  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
                  <maven.compiler.source>1.7</maven.compiler.source>
                  <maven.compiler.target>1.7</maven.compiler.target>
          
              </properties>
          
              <dependencies>
          
                  <!-- https://mvnrepository.com/artifact/junit/junit -->
                  <dependency>
                      <groupId>junit</groupId>
                      <artifactId>junit</artifactId>
                      <version>4.13.2</version>
                      <scope>test</scope>
                  </dependency>
          
                  <!--https://www.threeten.org/threetenbp/-->
                  <dependency>
                      <groupId>org.threeten</groupId>
                      <artifactId>threetenbp</artifactId>
                      <version>1.5.1</version>
                  </dependency>
          
          
              </dependencies>
          
              <build>
                  <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
                      <plugins>
                          <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
                          <plugin>
                              <artifactId>maven-clean-plugin</artifactId>
                              <version>3.1.0</version>
                          </plugin>
                          <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
                          <plugin>
                              <artifactId>maven-resources-plugin</artifactId>
                              <version>3.2.0</version>
                          </plugin>
                          <plugin>
                              <artifactId>maven-compiler-plugin</artifactId>
                              <version>3.8.1</version>
                          </plugin>
                          <plugin>
                              <artifactId>maven-surefire-plugin</artifactId>
                              <version>3.0.0-M5</version>
                          </plugin>
                          <plugin>
                              <artifactId>maven-jar-plugin</artifactId>
                              <version>3.2.0</version>
                          </plugin>
                          <plugin>
                              <artifactId>maven-install-plugin</artifactId>
                              <version>3.0.0-M1</version>
                          </plugin>
                          <plugin>
                              <artifactId>maven-deploy-plugin</artifactId>
                              <version>3.0.0-M1</version>
                          </plugin>
                          <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
                          <plugin>
                              <artifactId>maven-site-plugin</artifactId>
                              <version>3.9.1</version>
                          </plugin>
                          <plugin>
                              <artifactId>maven-project-info-reports-plugin</artifactId>
                              <version>3.1.2</version>
                          </plugin>
                      </plugins>
                  </pluginManagement>
              </build>
          </project>
          

          【讨论】:

            猜你喜欢
            • 2023-04-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-12-22
            • 2023-03-30
            • 2018-11-24
            • 2023-01-01
            • 2019-12-06
            相关资源
            最近更新 更多