Sort on a string that may contain a number

前端 未结 23 2053
走了就别回头了
走了就别回头了 2020-11-22 02:59

I need to write a Java Comparator class that compares Strings, however with one twist. If the two strings it is comparing are the same at the beginning and end of the strin

23条回答
  •  甜味超标
    2020-11-22 03:44

    The Alphanum algrothim is nice, but it did not match requirements for a project I'm working on. I need to be able to sort negative numbers and decimals correctly. Here is the implementation I came up. Any feedback would be much appreciated.

    public class StringAsNumberComparator implements Comparator {
    
        public static final Pattern NUMBER_PATTERN = Pattern.compile("(\\-?\\d+\\.\\d+)|(\\-?\\.\\d+)|(\\-?\\d+)");
    
        /**
         * Splits strings into parts sorting each instance of a number as a number if there is
         * a matching number in the other String.
         * 
         * For example A1B, A2B, A11B, A11B1, A11B2, A11B11 will be sorted in that order instead
         * of alphabetically which will sort A1B and A11B together.
         */
        public int compare(String str1, String str2) {
            if(str1 == str2) return 0;
            else if(str1 == null) return 1;
            else if(str2 == null) return -1;
    
            List split1 = split(str1);
            List split2 = split(str2);
            int diff = 0;
    
            for(int i = 0; diff == 0 && i < split1.size() && i < split2.size(); i++) {
                String token1 = split1.get(i);
                String token2 = split2.get(i);
    
                if((NUMBER_PATTERN.matcher(token1).matches() && NUMBER_PATTERN.matcher(token2).matches()) {
                    diff = (int) Math.signum(Double.parseDouble(token1) - Double.parseDouble(token2));
                } else {
                    diff = token1.compareToIgnoreCase(token2);
                }
            }
            if(diff != 0) {
                return diff;
            } else {
                return split1.size() - split2.size();
            }
        }
    
        /**
         * Splits a string into strings and number tokens.
         */
        private List split(String s) {
            List list = new ArrayList();
            try (Scanner scanner = new Scanner(s)) {
                int index = 0;
                String num = null;
                while ((num = scanner.findInLine(NUMBER_PATTERN)) != null) {
                    int indexOfNumber = s.indexOf(num, index);
                    if (indexOfNumber > index) {
                        list.add(s.substring(index, indexOfNumber));
                    }
                    list.add(num);
                    index = indexOfNumber + num.length();
                }
                if (index < s.length()) {
                    list.add(s.substring(index));
                }
            }
            return list;
        }
    }
    

    PS. I wanted to use the java.lang.String.split() method and use "lookahead/lookbehind" to keep the tokens, but I could not get it to work with the regular expression I was using.

提交回复
热议问题