无法使用 Collections(Java) 对文件名将包含时间戳的文件列表进行排序

Unable to sort list of files where the file name would contain the timestamp using Collections(Java)

我有一个包含文件列表的 arrayList,其名称确实是时间戳。

List<File> fileList = new ArrayList<>();
fileList.add(new File("20190612221053"));
fileList.add(new File("20190512221303"));
fileList.add(new File("20190612221353"));
fileList.add(new File("20190512222303"));
fileList.add(new File("20190612221303"));

时间戳的格式是'yyyymmddHHssmm'。

我的 objective 是按照文件名中给出的时间戳的升序对这个列表进行排序。

因此,我使用 Collections.sort 方法如下:

Collections.sort(fileList, new Comparator<File>() {
    @Override
    public int compare(File file1, File file2) {
        Date timeStamp1=null,timeStamp2=null;
        try {
            timeStamp1 = new SimpleDateFormat("yyyymmddHHssmm").parse(file1.getName());
        } catch (ParseException e) {
            e.printStackTrace();
        }
        try {
            timeStamp2 = new SimpleDateFormat("yyyymmddHHssmm").parse(file2.getName());
        } catch (ParseException e) {
            e.printStackTrace();
        }
        if(timeStamp1!=null && timeStamp2!=null && timeStamp1.getTime()!=timeStamp2.getTime() ) {
            return (timeStamp1.getTime() > timeStamp2.getTime()) ? 1 : -1;
        }
        else {
            return 0;
        }
    }
});

显然在这一行之后,您希望列表项(文件)的顺序为:

[20190512221303, 20190512222303, 20190612221053, 20190612221303, 20190612221353]

但我收到的是订单:

[20190512221303, 20190612221303, 20190512222303, 20190612221053, 20190612221353]

很明显出了点问题。 谁能指出我哪里做错了。

您可以尝试使用 java 8:

List<File> ordered = fileList.sort((o1, o2) ->
    Long.valueOf(Long.valueOf(o1.getName()) - Long.valueOf(o2.getName())).intValue());

基本上您将文件名转换为数字(yyyymmddHHssmm 结构保证较新的文件将具有更大的 Long 值),然后使用比较器计算差异并使用所述比较器调用 List#sort

ordered 将保存有序的文件列表

如果您想要倒序,请像这样交换运算符:

Long.valueOf(Long.valueOf(o2.getName()) - Long.valueOf(o1.getName())).intValue()


编辑

我们注意到文件名结构是这样的: yyyymmddHHmmss_someName.extension

因此,在执行比较之前,必须先从文件名中提取日期值。

在那种情况下,代码应该是:

    fileList.sort(
        (o1, o2) ->
            Long.valueOf(
                    Long.valueOf(o1.getName().split(Pattern.quote("_"))[0])
                        - Long.valueOf(o2.getName().split(Pattern.quote("_"))[0]))
                .intValue());

在此版本中,文件名在 _ 字符上拆分,日期参考被提取。然后将日期转换成数字,用于比较文件。


编辑2

如果文件名是动态的,并且名称中包含 yyyymmddHHmmss 引用,那么您可以这样做:

    Pattern pattern = Pattern.compile("\d{14}");

    fileList.sort(
        (o1, o2) -> {
          Matcher m1 = pattern.matcher(o1.getName());
          m1.find();
          String date1 = m1.group(0);
          Long num1 = Long.valueOf(date1);
          Matcher m2 = pattern.matcher(o2.getName());
          m2.find();
          String date2 = m2.group(0);
          Long num2 = Long.valueOf(date2);
          return Long.valueOf(num1 - num2).intValue();
        });

日期引用是从文件名的任何部分提取的。

您可以在 .find() 调用中添加额外的验证,以检查文件名是否符合预期的模式。


编辑3

可以通过使用可选添加文件名模式验证来改进解决方案:

    long minVal = 19700101000000L ;
    Pattern pattern = Pattern.compile("\d{14}");

    fileList.sort(
        (o1, o2) -> {
          Long num1 =
              Optional.of(pattern.matcher(o1.getName()))
                  .filter(Matcher::find)
                  .map(m -> Long.valueOf(m.group(0)))
                  .orElse(minVal);

          Long num2 =
              Optional.of(pattern.matcher(o2.getName()))
                  .filter(Matcher::find)
                  .map(m -> Long.valueOf(m.group(0)))
                  .orElse(minVal);

          return Long.valueOf(num1 - num2).intValue();
        });

在此解决方案中,如果任何文件名与预期模式不匹配,将使用默认值 minVal(非常旧的文件日期参考)。

希望对您有所帮助

代码中有两个主要错误

  1. 比较逻辑

使用

Long.compare(timeStamp1.getTime(), timeStamp2.getTime());

正确比较两个 long 数字

  1. 日期格式

切换 mm --> MM 月份。