【问题标题】:sort results in ascending return in the form A, a, B, b... in Java在 Java 中,排序结果以 A、a、B、b... 的形式升序返回
【发布时间】:2019-12-08 04:32:10
【问题描述】:

我想对 name 属性的 List 进行排序

并且排序应该像java中的A,a,B,b ...一样升序

如果有人在这方面帮助我,那就太好了。

例如

package corejava.compare;

import java.util.Comparator;

public class FirstNameSorter implements Comparator<Employee>{

     @Override
     public int compare(Employee o1, Employee o2) {
         return o1.getFirstName().compareTo(o2.getFirstName());
     }
}

我使用这个比较器如下:

Employee e1 = new Employee(1, "aTestName", "dLastName", 34);
Employee e2 = new Employee(2, "A", "pLastName", 30);
Employee e3 = new Employee(3, "T", "sLastName", 31);
Employee e4 = new Employee(4, "D", "zLastName", 25);
Employee e5 = new Employee(5, "b", "zLastName", 25);

List<Employee> employees = new ArrayList<Employee>();
employees.add(e2);
employees.add(e3);
employees.add(e1);
employees.add(e4);
employees.add(e5);

Collections.sort(employees, new FirstNameSorter());

// Sorted by firstName
System.out.println(employees);

然后我得到以下输出:

A
D
T
b

但是,我想要以下输出:

A
b
D
T

【问题讨论】:

  • 更新问题
  • 结果是A, a, b而不是a, A, b有关系吗?大写是否应该总是在其对应的小写之前(比如 A 在 a 之前,B 在 b 之前)?
  • 是的,大写应该先像A,a,B,b
  • 您当前的比较器按字典顺序进行比较,请参阅Wikipedia,因此它显然不是您想要的。您需要一种更复杂的比较器方法。例如,首先在忽略大小写的基础上进行比较(例如通过小写所有内容)。如果这两个元素相等,则按字典顺序比较它们(因为大写字母排在小写字母之前)。如果操作正确,比较器可能只有 1 到 3 行,并且也可以使用新的现代方法创建。

标签: java list collections comparator comparable


【解决方案1】:

就像评论中提到的@Zabuza 一样,您想要的顺序是几乎不区分大小写的字母顺序,但不完全是,因为您希望大小写字符在相同时组合在一起信。

您可以通过简单地添加小写名字并比较它们来完成此操作,如果小写检查显示匹配,则单独检查以恢复为纯字典顺序:

例如

public class FirstNameSorter implements Comparator<Employee> {
    @Override
    public int compare(Employee o1, Employee o2) {
        int precheck = o1.getFirstName().toLowerCase().compareTo(o2.getFirstName().toLowerCase());
        return precheck != 0 ? precheck : o1.getFirstName().compareTo(o2.getFirstName());
    }
}

【讨论】:

  • 员工 e1 = new Employee(1, "Aa", "dLastName", 34);员工 e2 = new Employee(2, "b", "pLastName", 30);员工 e3 = new Employee(3, "Bb", "sLastName", 31);员工 e4 = new Employee(4, "C", "zLastName", 25);它给了我输出 Aa,b,Bb,C 但输出应该是 Aa,Bb,b,C
  • 我希望大写字母在前。
【解决方案2】:

也许这对你有用:

public class Main {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("A", "D", "a", "b", "B", "T");
        list.sort(comparator());
        System.out.println(list);
    }

    private static Comparator<? super String> comparator() {
        return (Comparator<String>) (leftString, rightString) -> {
            if (leftString.length() == rightString.length()) {
                for (int index = 0; index < leftString.length(); index++) {
                    final String leftChar = leftString.substring(index, index + 1);
                    final String rightChar = rightString.substring(index, index + 1);
                    if (!leftChar.equals(rightChar)) {
                        if (leftChar.toUpperCase().equals(rightChar.toUpperCase())) {
                            if (leftChar.equals(leftChar.toLowerCase()) && 
                                    rightChar.equals(rightChar.toUpperCase())) {
                                return 1;
                            } else {
                                return -1;
                            }
                        } else {
                            return leftChar.toUpperCase()
                                    .compareTo(rightChar.toUpperCase());
                        }
                    }
                }
                return 0;
            } else {
                return (leftString.length() > rightString.length()) ? -1 : +1;
            }
        };
    }
}

输出:

[A, a, B, b, D, T]

【讨论】:

    【解决方案3】:

    更新(错误修复):

    问题
    仅使用caseInsensitiveThenNaturalOrder 的原始解决方案会将aAa 订购为[a, Aa],而不是预期的[Aa, a]

    原因
    String.CASE_INSENSITIVE_ORDER 只考虑相同长度的相同字符串,如aAaAAa,这是后面提供的条件thenComparing 比较器表示工作的词汇顺序。但由于aAa 的长度不同,它们不会按照将Aa 放在a 之前的词法顺序进行额外排序。

    解决方案
    为了解决这个问题,我们可以创建比较器,它将手动迭代两个字符串的“公共”索引,并使用之前创建的caseInsensitiveThenNaturalOrde(来自下面的原始答案)比较它们对应的字符。通过比较单个字符,我们确定它们的长度相同,这应该可以解决问题,让我们申请订单A &lt; a &lt; B &lt; b &lt; C &lt; c &lt; ...

    Comparator<String> myComparator = (s1, s2) -> {
        for (int i = 0; i < Math.min(s1.length(), s2.length()); i++) {
            int compare = caseInsensitiveThenNaturalOrder 
                    .compare(s1.substring(i, i + 1), s2.substring(i, i + 1));
            if (compare != 0) return compare;
        }
        return Integer.compare(s1.length(), s2.length());
    };
    
    //DEMO:
    String[] arr = {"a","A","aA","Aa","a","aA","bb","b","Bb","bB","aB"};
    Arrays.sort(arr, myComparator);
    System.out.println(Arrays.toString(arr));
    
    //Output:
    //[A, Aa, a, a, aA, aA, aB, Bb, b, bB, bb]
    

    原答案

    对于只有英文的字符,您可以使用 Comparator

    Comparator<String> caseInsensitiveThenNaturalOrder = String
                .CASE_INSENSITIVE_ORDER
                .thenComparing(Comparator.naturalOrder());
    

    它首先对单词进行排序,而不考虑它们的大小写,例如b,c,a,A -> a,A,b,c.thenComparing 提供了对被视为相等的元素进行排序的方法,例如 aA,其中 A 将放在 a 之前(这是字符串元素的自然排序)


    但如果名称可以包含非英文字符(如波兰语中的Ą,ą,应该放在A,aB,b 之间)我们不能使用String.CASE_INSENSITIVE_ORDER,因为它会将非英文字符放在英文后面那些。对于这种情况,我们需要Sort List of Strings with Localization 中描述的解决方案,例如

    Collator coll = Collator.getInstance(new Locale("pl", "PL")); //set locale of supported characters
    coll.setStrength(Collator.PRIMARY);
    Comparator<Object> caseInsensitiveThenNaturalOrder = coll
            .thenComparing((o1, o2) -> o1.toString().compareTo(o2.toString()));
    

    演示(支持波兰语字符):

    String[] arr = {"cC","Cc","cc","CC", "ab","aB","Ab","AB", "ą", "Ą"};
    Arrays.sort(arr, caseInsensitiveThenNaturalOrder);
    System.out.println(Arrays.toString(arr));
    

    输出:[AB, Ab, aB, ab, Ą, ą, CC, Cc, cC, cc]

    如果您想根据通过getFirstName 访问的属性对Employee 实例的集合进行排序,您可以使用caseInsensitiveThenNaturalOrder 类似的比较器

    Collections.sort(employees, Comparator.comparing(Employee::getFirstName, caseInsensitiveThenNaturalOrder));
    

    也可以写成

    employees.sort(Comparator.comparing(Employee::getFirstName, caseInsensitiveThenNaturalOrder));
    

    【讨论】:

    • 员工 e1 = new Employee(1, "Aa", "dLastName", 34);员工 e5 = new Employee(1, "a", "dLastName", 34);员工 e2 = new Employee(2, "b", "pLastName", 30);员工 e3 = new Employee(3, "Bb", "sLastName", 31);员工 e4 = new Employee(4, "C", "zLastName", 25);它给了我输出 a, Aa, b, Bb, C 但我想要输出 Aa, a, Bb, b, C
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-08-27
    • 2020-04-13
    • 1970-01-01
    • 2016-11-19
    • 1970-01-01
    • 1970-01-01
    • 2020-05-16
    相关资源
    最近更新 更多