Java自定义按相同字符串的2个部分排序

时间:2015-04-24 20:25:19

标签: java sorting arraylist

我已经看到过这样的其他问题,但无法将任何信息调整到我的代码中。要么是因为它不是我的问题所特有的,要么我无法理解答案。所以,我希望问"如何"用我的具体代码。告诉我是否需要更多。

我有各种文件(所有jpg' s),其名称格式为" 20140214-ddEventBlahBlah02.jpg"和" 20150302-ddPsBlagBlag2"。

我有一个使用的自定义比较器,以Windows操作系统的方式对事物进行排序...即02,2003,4,4b,4c,10等。而不是计算机的排序方式,这被搞砸了。一切都很好,除了我现在想要在字符串中使用2个条件对这些字符串进行排序。

1)日期(开头)。即20150302 2)" - "之后的文件名的其余部分即ddPsBlagBlag2

我目前正在将比较器用于以相反顺序显示这些文件的项目。它们根据最近添加的内容进行显示。即20150302在20140214之前显示。哪个好。但是我希望这些文件在按日期后按相反顺序排序后,按照正常的Windows操作系统升序顺序显示(不是相反)。

代码:

Collections.sort(file, new Comparator<File>() 
                    {
                    private final Comparator<String> NATURAL_SORT = new WindowsExplorerComparator();

                    @Override
                    public int compare(File o1, File o2) 
                    {
                        return NATURAL_SORT.compare(o1.getName(), o2.getName());
                    }
                });
                Collections.reverse(file);

上面的代码获取文件名的ArayList并将其发送到自定义的WindowsExplorerComparator类。排序后,在ArrayList上调用Collections.reverse()。

代码:

    class WindowsExplorerComparator implements Comparator<String> 
    {
    private static final Pattern splitPattern = Pattern.compile("\\d\\.|\\s");

@Override
public int compare(String str1, String str2) {
    Iterator<String> i1 = splitStringPreserveDelimiter(str1).iterator();
    Iterator<String> i2 = splitStringPreserveDelimiter(str2).iterator();
    while (true) 
    {
        //Til here all is equal.
        if (!i1.hasNext() && !i2.hasNext()) 
        {
            return 0;
        }
        //first has no more parts -> comes first
        if (!i1.hasNext() && i2.hasNext()) 
        {
            return -1;
        }
        //first has more parts than i2 -> comes after
        if (i1.hasNext() && !i2.hasNext()) 
        {
            return 1;
        }

        String data1 = i1.next();
        String data2 = i2.next();
        int result;
        try 
        {
            //If both datas are numbers, then compare numbers
            result = Long.compare(Long.valueOf(data1), Long.valueOf(data2));
            //If numbers are equal than longer comes first
            if (result == 0) 
            {
                result = -Integer.compare(data1.length(), data2.length());
            }
        } 
        catch (NumberFormatException ex) 
        {
            //compare text case insensitive
            result = data1.compareToIgnoreCase(data2);
        }

        if (result != 0) {
            return result;
        }
    }
}

private List<String> splitStringPreserveDelimiter(String str) {
    Matcher matcher = splitPattern.matcher(str);
    List<String> list = new ArrayList<String>();
    int pos = 0;
    while (matcher.find()) {
        list.add(str.substring(pos, matcher.start()));
        list.add(matcher.group());
        pos = matcher.end();
    }
    list.add(str.substring(pos));
    return list;
}

}

上面的代码是用于对ArrayList进行排序的自定义WindowsExplorerComperator类。

因此,我希望ArrayList在排序(和日期排序颠倒)之后的样子是:

20150424-ssEventBlagV002.jpg
20150323-ssEventBlagV2.jpg
20150323-ssEventBlagV3.jpg
20150323-ssEventBlagV10.jpg
20141201-ssEventZoolander.jpg
20141102-ssEventApple1.jpg

如您所见,首先按日期排序(并反转),然后按字符串名称的其余部分按升序排序。

这可能吗?请告诉我一个简单的解决方法。

3 个答案:

答案 0 :(得分:1)

你的关闭,每当处理不工作的东西时调试你的程序并确保方法返回你所期望的。当我运行你的程序时,我首先注意到的是,每次尝试将字符串转换为Long的迭代都会抛出NumberFormatException。这是一个大红旗,所以我扔了一些printlns来检查data1data2的价值是什么。

继承我的输出:

Compare: 20150323-ssEventBlagV 20150424-ssEventBlagV00
Compare: 20150323-ssEventBlagV 20150323-ssEventBlagV
Compare: 3. 2.
Compare: 20150323-ssEventBlagV 20150424-ssEventBlagV00
Compare: 20150323-ssEventBlagV 20150323-ssEventBlagV
Compare: 3. 2.
Compare: 20150323-ssEventBlagV1 20150323-ssEventBlagV
Compare: 20150323-ssEventBlagV1 20150424-ssEventBlagV00
Compare: 20141201-ssEventZoolander.jpg 20150323-ssEventBlagV1
Compare: 20141201-ssEventZoolander.jpg 20150323-ssEventBlagV
Compare: 20141201-ssEventZoolander.jpg 20150323-ssEventBlagV

这里需要注意的一点是,它试图将3.2.转换为长值,这当然不会起作用。

使用代码的最简单的解决方案是简单地更改正则表达式。虽然你可能会在未来使用更简单的字符串迭代路径而不是正则表达式,但我觉得正则表达式使这个问题复杂化而不是它有用。

新正则表达式:\\d+(?=\\.)|\\s

变更:

  • \\d - &gt; \\d+ - 在句号之前捕获所有数字而不仅仅是第一个数字
  • \\. - &gt; (?=\\.) - 在非捕获组中放置句点,以便您的方法不会将其附加到我们的数字

新的调试输出:

Compare: 20150323-ssEventBlagV 20150424-ssEventBlagV
Compare: 20150323-ssEventBlagV 20150323-ssEventBlagV
Compare: 3 2
Compare: 20150323-ssEventBlagV 20150323-ssEventBlagV
Compare: 10 3
Compare: 20141201-ssEventZoolander.jpg 20150323-ssEventBlagV

正如您所看到的那样,最后的数字实际上正在被正确解析。

还有一件小事:

您的数字比较结果是向后

result = Long.compare(Long.valueOf(data1), Long.valueOf(data2));

应该是:

result = -Long.compare(Long.valueOf(data1), Long.valueOf(data2));

result = Long.compare(Long.valueOf(data2), Long.valueOf(data1));

因为它会向后排序。

答案 1 :(得分:0)

你应该做一些事情:

首先,您需要将分割表达式修复为@ug_陈述。但是,我认为分割数字更合适。

private static final Pattern splitPattern = Pattern.compile("\\d+");

20150323-ssEventBlagV2.jpg将导致

[, 20150323, -ssEventBlagV, 2, .jpg]

其次,执行与Long比较分开的日期比较。使用SimpleDateFormat将确保您只比较格式化为日期的数字。

try {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
    result = sdf.parse(data2).compareTo(sdf.parse(data1));
    if (result != 0) {
        return result;
    }
} catch (final ParseException e) {
    /* continue */
}

最后,交换长期比较的顺序

Long.compare(Long.valueOf(data2), Long.valueOf(data1));

你应该好好去。完整代码如下。

private static final Pattern splitPattern = Pattern.compile("\\d+");

    @Override
    public int compare(String str1, String str2) {
        Iterator<String> i1 = splitStringPreserveDelimiter(str1).iterator();
        Iterator<String> i2 = splitStringPreserveDelimiter(str2).iterator();
        while (true) {
            // Til here all is equal.
            if (!i1.hasNext() && !i2.hasNext()) {
                return 0;
            }
            // first has no more parts -> comes first
            if (!i1.hasNext() && i2.hasNext()) {
                return -1;
            }
            // first has more parts than i2 -> comes after
            if (i1.hasNext() && !i2.hasNext()) {
                return 1;
            }

            String data1 = i1.next();
            String data2 = i2.next();
            int result;

            try {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
                result = sdf.parse(data1).compareTo(sdf.parse(data2));
                if (result != 0) {
                    return result;
                }
            } catch (final ParseException e) {
                /* continue */
            }

            try {
                // If both datas are numbers, then compare numbers
                result = Long.compare(Long.valueOf(data2),
                        Long.valueOf(data1));
                // If numbers are equal than longer comes first
                if (result == 0) {
                    result = -Integer.compare(data1.length(),
                            data2.length());
                }
            } catch (NumberFormatException ex) {
                // compare text case insensitive
                result = data1.compareToIgnoreCase(data2);
            }

            if (result != 0) {
                return result;
            }

        }
    }

答案 2 :(得分:-1)

您需要编辑WindowsExporerComparator类,以便执行此排序。给定两个文件名作为字符串,您需要使用以下高级算法确定它们的顺序。

  1. 他们是一样的吗?如果是,则返回0
  2. 将文件名拆分为两个字符串,即日期部分和名称部分。
  3. 使用日期部分使用Java DateTime将字符串转换为日期,然后比较日期。
  4. 如果日期相同,则使用您当前的比较代码比较两个名称部分,并从中返回结果。
  5. 这有点复杂,有点令人困惑,但你必须在一个比较器中完成并放入所有自定义逻辑