自然排序和Java中的实现
今天工作中突然遇到一个需求,需要对一个数据集合进行排序,排序的依据是一个字符串字段,由于这个数据集合是利用sql在MySQL中查找的结果,所以很自然而然的想到使用order by语句进行排序
SELECT column1,column2,column3,...
FROM table
WHERE cases
ORDER BY need_sorted_field
正好这种排序结果也满足了客户的需求,所以这个需求也是顺利完成
但是事情远没有那么简单。。。
这种排序机制是建立在字符串的格式完全一致的情况下的,如果不一致,就会出现意想不到的结果,于是我果断查找了资料,发现order by子句的排序算法使用的其实是自然排序(也称字典排序)
自然排序
copy by wiki~~
设想一本英语字典里的单词,哪个在前哪个在后?
显然的做法是先按照第一个字母、以 a、b、c……z 的顺序排列;如果第一个字母一样,那么比较第二个、第三个乃至后面的字母。如果比到最后两个单词不一样长(比如,sigh 和 sight),那么把短者排在前。
不难理解,order by子句就是把要排序的列当成我们平时使用的字典,然后像字典那样去排序
Java中的实现
Java提供了Comparable
接口提供给开发者自定义排序策略
public interface Comparable<T> {
public int compareTo(T o);
}
幸运的是,Java针对字符串也继承了该接口
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence
并且提供了简单的排序算法
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
其实不难发现,这种比较策略大致和sql中的order by的比较策略是基本一样的,因此,以后再面对相同的需求的时候,可以考虑直接使用String
类中的排序策略进行排序
除此以外,Java还提供了一种忽略大小写的字符串排序方式,也是实现在String
类中
/**
* A Comparator that orders {@code String} objects as by
* {@code compareToIgnoreCase}. This comparator is serializable.
* <p>
* Note that this Comparator does <em>not</em> take locale into account,
* and will result in an unsatisfactory ordering for certain locales.
* The java.text package provides <em>Collators</em> to allow
* locale-sensitive ordering.
*
* @see java.text.Collator#compare(String, String)
* @since 1.2
*/
public static final Comparator<String> CASE_INSENSITIVE_ORDER
= new CaseInsensitiveComparator();
private static class CaseInsensitiveComparator
implements Comparator<String>, java.io.Serializable {
// use serialVersionUID from JDK 1.2.2 for interoperability
private static final long serialVersionUID = 8575799808933029326L;
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);
if (c1 != c2) {
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
if (c1 != c2) {
c1 = Character.toLowerCase(c1);
c2 = Character.toLowerCase(c2);
if (c1 != c2) {
// No overflow because of numeric promotion
return c1 - c2;
}
}
}
}
return n1 - n2;
}
/** Replaces the de-serialized object. */
private Object readResolve() { return CASE_INSENSITIVE_ORDER; }
}
调用方式很简单
String.CASE_INSENSITIVE_ORDER.compare(str1,str2);//str1和str2为对应的字符串
其实不难发现,这种方式就是在原来的compareTo
方法里将字符都转成了大写
参考文献
字典序 by wiki