使用Java中的Comparator自定义排序字母数字字符串

时间:2018-08-06 13:29:52

标签: java

假设我有一个字符串列表,例如'ABC123','XXY111','EFG001'等。
我需要用两种方式对这些字符串进行排序,
1)首先,它将按数字排序。
2)然后将按字母排序。

我尝试使用比较器对字符串进行排序。
首先,我分割字符串并在开头加上数字,然后对
进行排序  使用Collections.sort()列出。

但是我不确定如何用2种方式对其进行排序。

下面是我的代码,

  public class SortAlphaNumeric {   

    static class MyComparator implements Comparator<String> {

        @Override
        public int compare(String o1, String o2) {

            String oo1 = o1.substring(3) + o1.substring(0,3);
            String oo2 = o2.substring(3) + o2.substring(0,3);

            return oo1.compareTo(oo2);
        }       
    }

    public static void main(String[] args) {

        String str1 = "ABC123";
        String str2 = "ACB111";
        String str3 = "XXX003";
        String str4 = "XYZ001"; 
        String str5 = "CDE123";
        String str6 = "FEG111"; 


        List<String> list = new ArrayList<String>();        

        list.add(str1);
        list.add(str2);
        list.add(str3);
        list.add(str4);
        list.add(str5);
        list.add(str6);

        System.out.println("Before sorting");

        Iterator<String> itr1 = list.iterator();

        while(itr1.hasNext()) {
            System.out.println(itr1.next());
        }

        SortAlphaNumeric.MyComparator myComp = new SortAlphaNumeric.MyComparator();

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

        Collections.sort(list, myComp); 

        System.out.println("After 1st sorting");

        Iterator<String> itr2 = list.iterator();

        while(itr2.hasNext()) {
            System.out.println(itr2.next());
        }

        Collections.sort(list, myComp);

        System.out.println("After 2nd sorting");

        Iterator<String> itr3 = list.iterator();

        while(itr3.hasNext()) {
            System.out.println(itr3.next());
        }
    }
}

3 个答案:

答案 0 :(得分:2)

您可以轻松地组成两个比较器,将0-3索引比较链接到3-6索引比较上。

以下是使用子字符串而不是split的替代方法(进一步简化了代码):

Comparator<String> myComp = Comparator.comparing((String s) -> s.substring(3))
                                      .thenComparing(s -> s.substring(0, 3));

// resulting in:
// XYZ001, XXX003, ACB111, FEG111, ABC123, CDE123

假设文本具有此标准长度,则以上代码消除了对MyComparator类的需要。

答案 1 :(得分:1)

如果您使用的是Java 8,则可以使用Comparator:

List<String> inputs = Arrays.asList(
        "ABC123", "ACB111", "XXX003", "XYZ001", "CDE123", "FEG111"
);    
inputs.sort(Comparator.comparing((String s) -> Integer.valueOf(s.split("(?<=\\D)(?=\\d)")[1]))
        .thenComparing(s -> s.split("(?<=\\D)(?=\\d)")[0]));

System.out.println(inputs);

输出

[XYZ001, XXX003, ACB111, FEG111, ABC123, CDE123]

答案 2 :(得分:1)

正如您已经阐明的那样,您不想使用逻辑“按数字部分排序,如果相等,则按char部分排序”来进行一次排序,但是应该假定它是两种不同的排序。为此,您应该实现两个不同的比较器。

数值比较

只要所有字符串的长度和char-number-distribution相同,当前的逻辑(在索引3处分割并将数字与String进行比较)就可以工作。但是,如果存在诸如“ ABC12”或“ A12345”之类的元素,则会遇到麻烦:首先,您将字符和数字混合在一起,然后,即使您设法正确地将它们分开(请阅读here如何做到),将数字与String进行比较,如果它们的长度不同(例如2 <12,但“ 2”>“ 12”),则会导致错误的结果。您应该将数字解析为实际的整数,然后进行比较。这是用于此目的的比较器的外观:

Comparator<String> byNumber = Comparator.comparingInt(o -> Integer.parseInt(getNumberPart(o)));
// look at the linked question for how to properly split, a simple
// solution might use something like o.replaceAll("\\w", "")

字符串比较

至于char部分,这很简单:

Comparator<String> byChars = Comparator.comparing(o -> getCharPart(o));

现在,您可以使用它们根据需要对列表进行排序:

list.sort(byNumber); // XYZ001, XXX003, FEG111<->ACB111, ABC123<->CDE123
list.sort(byChars);  // ACB111<->ABC123, CDE123, FEG111, XYZ003<->XXX001

请注意,由于仅按数字或字符进行比较,因此不确定如何对具有相同数字/字符部分的元素进行排序。例如,它可以根据并行性或基础集合进行更改。如果要确保始终有相同的顺序,可以将主比较器与第二个比较器组合,如果第一个返回0,则使用第二个比较器:

list.sort(byNumber.thenComparing(byChars)); // XYZ001, XXX003, ACB111, FEG111, ABC123, CDE123
list.sort(byChars.thenComparing(byNumber)); // ACB111, ABC123, CDE123, FEG111, XYZ001, XXX003