简体   繁体   English

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

[英]Custom sorting Alphanumeric Strings using Comparator in Java

Suppose that I have a list of strings 'ABC123' , 'XXY111' , 'EFG001 ' and so on. 假设我有一个字符串列表'ABC123''XXY111''EFG001 '等。

I need to sort these strings in 2 ways, 我需要以两种方式对这些字符串进行排序,

1) First sorted by the numbers. 1)首先按数字排序。
2) Then it sorted by the letters. 2)然后按字母排序。

I tried using a Comparator to sort the strings. 我尝试使用比较器对字符串进行排序。

First I split the string and had the numbers at the beginning and then sorted the List using Collections.sort() . 首先,我分割字符串并在开头加上数字,然后使用Collections.sort()对List进行排序。

But I am not sure how to sort it in the two ways. 但是我不确定如何用两种方式对其进行排序。

Below is my code, 下面是我的代码,

  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());
        }
    }
}

You can easily compose two comparators, chaining the 0-3 index comparison on the 3-6 index comparison. 您可以轻松地组成两个比较器,将0-3索引比较链接到3-6索引比较上。

The following is an alternative using substrings instead of split (simplifying the code even further): 以下是使用子字符串而不是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

Assuming the texts have this standard length, the above code eliminates the need for MyComparator class. 假定文本具有此标准长度,则以上代码消除了对MyComparator类的需要。

If you are using Java 8 you can use Comparator like this : 如果您使用的是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);

Outputs 产出

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

As you have clarified, you do not want to sort once with the logic "sort by the numeric part, and if that is equal, sort by the char part", but instead it is supposed to be two different sorts. 正如您已经阐明的那样,您不想使用逻辑“按数字部分排序,如果相等,则按字符部分排序”来进行一次排序,但是应该假定它是两种不同的排序。 You should implement two different comparators for that. 您应该为此实现两个不同的比较器。

Numeric comparison 数值比较

Your current logic (splitting at index 3 and comparing the number as String ) works as long as all strings have the same length and char-number-distribution. 只要所有字符串的长度和char-number-distribution都相同,您当前的逻辑(在索引3处分割并将数字与String进行比较)就可以工作。 If there where elements like "ABC12" or "A12345", though, you'd run into trouble: First, you'd mix chars and digit, and second, even if you managed to split them correctly (read here how to do that), comparing numbers as String will lead to wrong results if they differ in length (eg 2<12, but "2">"12"). 但是,如果存在诸如“ ABC12”或“ A12345”之类的元素,则会遇到麻烦:首先,您将字符和数字混合在一起,其次,即使您设法正确地将它们分开(请在此处阅读如何操作) ),将数字与String进行比较,如果它们的长度不同(例如2 <12,但“ 2”>“ 12”),则会导致错误的结果。 You should instead parse the number to an actual integer, then compare that. 您应该将数字解析为实际的整数,然后进行比较。 This is how a comparator for this purpose could look: 这是用于此目的的比较器的外观:

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", "")

String comparison 字符串比较

As for the char part, that one is straightforward: 至于字符部分,这很简单:

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

Now you can use those to sort your list as you like: 现在,您可以使用它们根据需要对列表进行排序:

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

Note that since you only compare by either number or chars , there is uncertainty how the elements with equal number/char part will be sorted. 请注意,由于仅按数字或字符进行比较,因此不确定如何对具有相同数字/字符部分的元素进行排序。 It can change depending on parallelism or the underlying collection, for example. 例如,它可以根据并行性或基础集合进行更改。 If you want to make sure that you always have the same order, you can combine your main comparator with a second one that is used if the first one returns 0: 如果要确保始终有相同的顺序,可以将主比较器与第二个比较器组合,如果第一个返回0,则使用第二个比较器:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM