Sorting String keys of a Map according to their nu

2019-06-10 21:43发布

问题:

I need to write a class that handles a Map<String, String[]>, processing its keys according to their numeric order. To add insult to injury, some keys are not valid integers, and they should be processed at the end, in acceding lexicographic order.

For example, if the keys are:

["10", "2", "100", "duck", "black"]

They should by iterated at this order -

["2", "10", "100", "black", "duck"]

What's the most elegant way to do it in Java, other than iterating and try-catching NumberFormatException? Obviously, I can not control the format of the given map.

回答1:

Since you need to iterate in a particular order that isn't the input map's natural order, you'll need to dump it into another map (or a list if you don't need the associated values for each key). Use a TreeMap with a custom comparator:

class NumbersThenWordsComparator implements Comparator<String> {
    private static Integer intValue(String s) {
        try {
            return Integer.valueOf(s);
        } catch (NumberFormatException e) {
            return null;
        }
    }

    @Override
    public int compare(String s1, String s2) {
        Integer i1 = intValue(s1);
        Integer i2 = intValue(s2);
        if (i1 == null && i2 == null) {
            return s1.compareTo(s2);
        } else if (i1 == null) {
            return -1;
        } else if (i2 == null) {
            return 1;
        } else {
            return i1.compareTo(i2);
        }
    }       
}

public void myMethod(Map<String, String[]> originalMap) {
    TreeMap<String, String[]> t =
        new TreeMap<String, String[]>(new NumbersThenWordsComparator());
    t.putAll(originalMap);
    // now iterate over t, which will produce entries in the desired order
}


回答2:

Use a SortedMap, which sorts keys based on their natural ordering. For Strings, it's lexicographic. I'm not sure if numbers come before letters, but either way sorting will be well defined and consistent, so you can handle it properly to get numbers first or last (i.e. do something like ArrayList<String> keys = yourMap.keySet() and then modify it if the number strings aren't first).