字符串排序
需求如下: 给联系人排序, 按照姓名来排序. 要求小写字母排前面,大写字母排后面.
看到这个,我脑海里预想的排序结果是这样的
[aaa,aab,doc,zz,Apple]
也就是说 z
是小于 A
的,在这种需求下.
但是,我看到为手机联系人的排序不是这样的,是a < A < b
的这种效果,而不是之前预期的 a < b < .. < z < A < B .. < Z
的这种效果.
当然,具体要什么效果不重要,重要的怎么去实现.
a 的这种效果.
java
里面默认的字符串排序效果是这样的before:[abc, ABC, adc, Abc, Abc张三, aa, aBc]
end: [ABC, Abc, Abc张三, aBc, aa, abc, adc]
java
里面忽略大小写的排序效果是这样的before:[abc, ABC, adc, Abc, Abc张三, aa, aBc]
after: [aa, abc, ABC, Abc, aBc, Abc张三, adc]
ABC
不应该在 Abc
的前面,不符合我们的需求. ===> FAILED那么,思考一下,这个东西要怎么实现好呢? 按照字符编码, a
与 b
之间的 间隔是 1
, A
与B
的间隔当然也是 1
.
int x = `A` - `B`; // -1
int y = `a` - `b`; // -1
int z = `a` - `A`; // 32
但是现在希望在 a
,b
之间插入一个 A
进来.
我说一下几个思路,我当时想到的:
把源字符串转成 26*2
进制的数字, 然后直接比较数字的大小. 比如 "abc"
实际上就成了一个比较大的数字了,具体多少要计算一下,然后全部的字符串都是这样的.
最后没这么去实现.感觉有点麻烦.而且,如果字符串里面出现了非字母的字符咋办,不知所措.
放大比较. 既然 a
与 b
的间隔只差了1
,那么将比较结果放大10
倍,那么中间就能插入A
了. 实际用的就是这种方案
也想到了遇到非字母的字符要咋办,感觉不用管,在这种实现里面.
具体实现代码不多,但是很有用:
private static class MyComparator
implements Comparator<String> {
private static final int FACTOR = 10;
private static final int DIFF = FACTOR / 2;
public int compare(String s1, String s2) {
int n1 = s1.length();
int n2 = s2.length();
int min = Math.min(n1, n2);
for (int i = 0; i < min; i++) {
char c1 = s1.charAt(i);
char c2 = s2.charAt(i);
boolean u1 = Character.isUpperCase(c1);
boolean u2 = Character.isUpperCase(c2);
System.out.println(String.format("COMPARE: %s(%s),%s(%s)", s1, c1, s2, c2));
// A a, A b, A c
if (c1 != c2) {
// No overflow because of numeric promotion
if (!u1 && !u2) {
int i1 = FACTOR * (c1 - c2);
System.err.println(String.format("compare1: %s(%s),%s(%s) ====> %s", s1, c1, s2, c2, i1));
return i1;
} else if (u1) {
// D c == > d+x c (67 - 66) * 10 + 5 15
// D e ==> d+x e (67 - 68) * 10 + 5 -5
// D a ==> d+x a (67 - 64) * 10 + 5*1? 35
int i1 = FACTOR * (Character.toLowerCase(c1) - c2) + DIFF;
System.err.println(String.format("compare2: %s(%s),%s(%s) ====> %s", s1, c1, s2, c2, i1));
return i1;
} else {
// d E == > d e+5 === > d e+x (67 - 68) * 10 -5 -15
// d C ==> d c+5 ===> d c+x (67 - 66) * 10 -5 5
// d A ==> d a+5 ==========> (67 - 64) * 10 -5 25
int i1 = FACTOR * (c1 - Character.toLowerCase(c2)) - DIFF;
System.err.println(String.format("compare3: %s(%s),%s(%s) ====> %s", s1, c1, s2, c2, i1));
return i1;
}
}
}
return n1 - n2;
}
}
调用很简单:
List<String> list3 = Arrays.asList("abc", "ABC", "adc", "Abc", "Abc张三", "aa", "aBc");
List<String> strings2 = new ArrayList<>(list3);
System.out.println("BEFORE @@@@@@@@@@@@@@@@@@@@@ : " + strings2);
strings2.sort(new MyComparator());
System.out.println("MyComparator END $$$$$$$$ : " + strings2);
实际输出:
BEFORE @@@@@@@@@@@@@@@@@@@@@ : [abc, ABC, adc, Abc, Abc张三, aa, aBc]
MyComparator END $$$$$$$$ : [aa, abc, aBc, adc, Abc, Abc张三, ABC]
看到结果了吧,确实实现了 a < A < b < B
的效果. ===> PASSED
以上.