Java SortedMap Comparator 让数字键在字母键之后排序

Java SortMap Comparator to have digit keys ordered after letter keys

我需要一个 SortedMap,其中字符键按以下方式排序:('A'..'Z'..'0'..'9'),所以首先是字符,然后是数字,全部按升序排列。 这是我到目前为止所尝试的,但是,输出显示它无法返回我想要的排序,因为数字键的值仍然在字母键的值之前。 我究竟做错了什么?有没有更好的方法来做到这一点? 提前致谢!

public static void main(String[] args) {
    SortedMap<Character, String> sortedMap = new TreeMap<>(new Comparator<Character>() {
        @Override
        public int compare(Character o1, Character o2) {
            if (Character.isDigit(o1) && Character.isLetter(o2)){
                return o2.compareTo(o1);
            }
            else if(Character.isLetter(o1) && Character.isDigit(o2)){
                return o1.compareTo(o2);
            }
            else{
                return o1.compareTo(o2);
            }
        }
    });

    sortedMap.put('5', "five");
    sortedMap.put('8', "nine");
    sortedMap.put('A', "ALPHA");
    sortedMap.put('G', "GOLF");
    sortedMap.put('F', "FOXTROT");
    System.out.println(sortedMap.values());
}

如果 o1 是数字且 o2 是字母,则需要 return 结果 "o1 > o2",因此在本例中为 return 1。同理,如果o1是字母,o2是数字,需要return结果"o1 < o2",即return -1.

public int compare(Character o1, Character o2) {
   if (Character.isDigit(o1) && Character.isLetter(o2)){
      return 1;
   }
   else if(Character.isLetter(o1) && Character.isDigit(o2)){
      return -1;
   }
   else{
      return o1.compareTo(o2);
   }
}

如果 o1 是一个字母并且 o2 是一个数字,你应该总是 return -1,因为你 总是 想要字母先走。如果相反,你应该总是 return 1 。

唯一应该return o1.compareTo(o2)的情况是最后的else,这意味着o1o2要么是字母要么是数字,在那种情况下,比较器应该简单地遵循自然顺序。

当您希望所有字母都出现在所有数字之前,并且您得到一位数字和一个字母时,您根本不应该比较它们:

public int compare(Character o1, Character o2) {
    if (Character.isDigit(o1) && Character.isLetter(o2)) {
        // o1 < o2 regardless of what values o1 and o2 are
        // So return any negative integer
        return -100;
    }
    else if (Character.isLetter(o1) && Character.isDigit(o2)) {
        // o1 > o2 regardless of what values o1 and o2 are
        // So return any positive integer
        return 100;
    }
    // Both must be letters or both must be integers
    // fall back to normal comparison, as you have already done
    return o1.compareTo(o2);
}

我更喜欢在 Map 声明之外使用 ternary (?:) 构造来构建 Comparator。我还添加了一些额外的值,散布在您的值中以增加可变性。

        Comparator<Character> comp = (a, b) -> Character.isLetter(a)
                && Character.isDigit(b) ? -1 :
                Character.isLetter(b) && Character.isDigit(a) ? 1 :
                a.compareTo(b);

        SortedMap<Character, String> sortedMap = new TreeMap<>(comp);
        sortedMap.put('Q', "Quebec");
        sortedMap.put('B', "Beta");
        sortedMap.put('5', "five");
        sortedMap.put('9', "nine");
        sortedMap.put('A', "ALPHA");
        sortedMap.put('3', "three");
        sortedMap.put('G', "GOLF");
        sortedMap.put('F', "FOXTROT");
        sortedMap.entrySet().forEach(System.out::println);

版画


A=ALPHA
B=BETA
F=FOXTROT
G=GOLF
Q=QUEBEC
3=three
5=five
9=nine

假设您使用的是 Java 8+,请将您的地图声明为:

SortedMap<Character, String> sortedMap = 
            new TreeMap<>(Comparator.comparingInt(c -> (c + 197) % 255));

可选地,您可以将 "magic numbers" 197 和 255 提取为常量,或将 comparingInt(..) 的参数提取为常量。

解释:
字符 0..9 对应 ASCII 码 48..57
所有字母的 ASCII 码都属于范围 65..122
上面的代码稍微移动了代码,将数字范围移动到 245..254,将字母范围移动到 7..64
由于 Comparator 比较移位后的代码,它会将字母放在数字之前