4

I need a SortedMap where the Charachter keys are sorted this way: ('A'..'Z'..'0'..'9'), so character first, then digits, all in ascending order. This is what I have tried so far, however, the output shows it fails in returning the sorting I want, because the values of the digit keys are still before the values of letter keys. What am I doing wrong? Is there an even better way to do this? Thanks in advance!

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());
}
Alex
  • 3,164
  • 1
  • 3
  • 15
pppccc
  • 43
  • 2

5 Answers5

2

You need to return the result "o1 > o2" if o1 is a digit and o2 is a letter, so return 1 in this case. Similarly, if o1 is a letter and o2 is a digit, you need to return the result "o1 < o2", i.e. 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);
   }
}
Alex
  • 3,164
  • 1
  • 3
  • 15
2

You should always return -1 if o1 is a letter and o2 is a digit, because you always want the letter to go first. And you should always return 1 if it's the opposite.

The only case where it should return o1.compareTo(o2) is in the final else, which means o1 and o2 are both either letters or numbers, and in that case, the comparator should simply follow natural order.

mjuarez
  • 14,655
  • 10
  • 52
  • 65
1

Asusuming you're using Java 8+, declare your Map as:

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

Optionally, you can extract "magic numbers" 197 and 255 into constants, or extract argument of comparingInt(..) into a constant.

Explanation:
chars 0..9 correspond to ASCII codes 48..57
ASCII codes for all the letters belong to range 65..122
Code above shifts codes a bit, moving numbers range to 245..254 and letters range to 7..64
Since Comparator compares shifted codes, it places letters before digits

Slava Medvediev
  • 994
  • 9
  • 27
0

When you want all letters to come before all digits, and you get one digit and one letter, you should not be comparing them at all:

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);
}
havanagrawal
  • 1,013
  • 6
  • 12
0

I prefer using a ternary (?:) construction outside the Map declaration to build the Comparator. I also added some extra values interspersed with yours to add variability.

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

Prints


A=ALPHA
B=BETA
F=FOXTROT
G=GOLF
Q=QUEBEC
3=three
5=five
9=nine
WJS
  • 22,083
  • 3
  • 14
  • 32