2019-04-11

自然排序和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 {
    public int compareTo(T o);
}

幸运的是,Java针对字符串也继承了该接口

public final class String
    implements java.io.Serializable, Comparable, 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.
     * 

* Note that this Comparator does not take locale into account, * and will result in an unsatisfactory ordering for certain locales. * The java.text package provides Collators to allow * locale-sensitive ordering. * * @see java.text.Collator#compare(String, String) * @since 1.2 */ public static final Comparator CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator(); private static class CaseInsensitiveComparator implements Comparator, 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

你可能感兴趣的:(2019-04-11)