十分钟探讨map与hashmap的排序

来源:互联网 发布:淘宝福利晒图店家 编辑:程序博客网 时间:2024/06/05 19:29

今天遇到一个关于map排序的问题,做个笔记(^__^) 嘻嘻……

既然遇到了,就不如挖它祖坟看一看里面的究竟,说不定找到价值连城的古董或者什么的,也说不准,下面,我们就一起带着铲子和摸金校尉,一起去探个究竟吧^_^

老规矩,先上代码,有代码有真相,如下所示:

/** *  * Created by zero on 2016-6-11 * */public class HashMapSort{    public static void main(String[] args) {        Map<String, Object> map = new HashMap<String, Object>();        map.put("b", 1 + "");        map.put("cds", 1 + "");        map.put("daac", 23 + "");        map.put("ass", 3 + "");        map.put("daaa", 21 + "");        map.put("f", 2 + "");        map.put("daaz", 25 + "");        map.put("e", 22 + "");        List<Map.Entry<String, Object>> list = new ArrayList<Map.Entry<String, Object>>(                map.entrySet());        Collections.sort(list, new Comparator<Map.Entry<String, Object>>()        {            public int compare(Entry<String, Object> o1,                    Entry<String, Object> o2) {//              // 按key升序排序                return o1.getKey().compareTo(o2.getKey());//              // 按key降序排序//              return o2.getKey().compareTo(o1.getKey());//              // 把value转化成String升序排序//              return ((String) o1.getValue()).compareTo((String) o2.getValue());//              // 把value转化成String降序排序//              return ((String) o2.getValue()).compareTo((String) o1.getValue());            }        });        for (Map.Entry<String, Object> mapping : list)        {            System.out.println(mapping.getKey() + "=" + mapping.getValue());        }    }}

我们采用的是key的升序排序,执行后的结果是这个样子的

ass=3b=1cds=1daaa=21daac=23daaz=25e=22f=2

那很多人要说了,我们的产品需要的是降序或者是value排序,那也很简单,只要改return那行代码就好,但是有一点要强调的是,compareTo前后的数据类型要一致,String、Long、Integer等都支持,或者更准确的说,只要实现Comparable接口的都支持,这个接下来会举例分析,现在,我们先看下sort这个函数,在Collections中有两个sort方法,如下所示:

public static <T extends Comparable<? super T>> void sort(List<T> list) {        Object[] a = list.toArray();        Arrays.sort(a);        ListIterator<T> i = list.listIterator();        for (int j=0; j<a.length; j++) {            i.next();            i.set((T)a[j]);        }    }public static <T> void sort(List<T> list, Comparator<? super T> c) {        Object[] a = list.toArray();        Arrays.sort(a, (Comparator)c);        ListIterator<T> i = list.listIterator();        for (int j=0; j<a.length; j++) {            i.next();            i.set((T)a[j]);        }    }

第一个sort方法,是根据元素的自然顺序对指定列表按升序进行排序。列表中的所有元素都必须实现 Comparable 接口(根据刚刚的介绍,是不是想到了什么?String、Long、Integer等,当然,也可以自己手写去实现接口)。此外,列表中的所有元素都必须是可相互比较的(也就是说,对于列表中的任何 e1 和 e2 元素,e1.compareTo(e2) 不得抛出 ClassCastException)。
此排序方法具有稳定性:不会因调用 sort 方法而对相等的元素进行重新排序。

指定列表必须是可修改的,但不必是大小可调整的。

该排序算法是一个经过修改的合并排序算法(其中,如果低子列表中的最高元素小于高子列表中的最低元素,则忽略合并)。此算法提供可保证的 n log(n) 性能。 此实现将指定列表转储到一个数组中,并对数组进行排序,在重置数组中相应位置处每个元素的列表上进行迭代。这避免了由于试图原地对链接列表进行排序而产生的 n2 log(n)(PS:此处2为平方,忘记平方怎么打了,O(∩_∩)O~) 性能。

我们着重看下第二个sort方法,参数:
·list - 要排序的列表。
·c - 确定列表顺序的比较器。null 值指示应该使用元素的自然顺序。

抛出:
·ClassCastException - 如果列表中包含不可使用指定比较器相互比较 的元素。
·UnsupportedOperationException - 如果指定列表的列表迭代器不支持 set 操作。

根据指定比较器产生的顺序对指定列表进行排序。此列表内的所有元素都必须可使用指定比较器相互比较(也就是说,对于列表中的任意 e1 和 e2 元素,c.compare(e1, e2) 不得抛出 ClassCastException)。
此排序被保证是稳定的:不会因调用 sort 而对相等的元素进行重新排序。

排序算法是一个经过修改的合并排序算法(其中,如果低子列表中的最高元素小于高子列表中的最低元素,则忽略合并)。此算法提供可保证的 n log(n) 性能。 指定列表必须是可修改的,但不必是可大小调整的。此实现将指定列表转储到一个数组中,并对数组进行排序,在重置数组中相应位置每个元素的列表上进行迭代。这避免了由于试图原地对链接列表进行排序而产生的 n2 log(n) 性能。

下面,我们在通过最初代码将Object转换成String,通过value降序排序,就像上述说所得那样,只要打开return就ok,如下:

public class HashMapSort{    public static void main(String[] args) {        Map<String, Object> map = new HashMap<String, Object>();        map.put("b", 1 + "");        map.put("cds", 1 + "");        map.put("daac", 23 + "");        map.put("ass", 3 + "");        map.put("daaa", 21 + "");        map.put("f", 2 + "");        map.put("daaz", 25 + "");        map.put("e", 22 + "");        List<Map.Entry<String, Object>> list = new ArrayList<Map.Entry<String, Object>>(                map.entrySet());        Collections.sort(list, new Comparator<Map.Entry<String, Object>>()        {            public int compare(Entry<String, Object> o1,                    Entry<String, Object> o2) {//              // 按key升序排序//              return o1.getKey().compareTo(o2.getKey());//              // 按key降序排序//              return o2.getKey().compareTo(o1.getKey());//              // 把value转化成String升序排序//              return ((String) o1.getValue()).compareTo((String) o2.getValue());//              // 把value转化成String降序排序                return ((String) o2.getValue()).compareTo((String) o1.getValue());            }        });        for (Map.Entry<String, Object> mapping : list)        {            System.out.println(mapping.getKey() + "=" + mapping.getValue());        }    }}

运行结果

ass=3daaz=25daac=23e=22daaa=21f=2b=1cds=1

这个value降序排序是不是有点出乎想象?我们采用的是Object转String,这真的是value降序排序吗?带着这个疑问去打开String源码找到compareTo方法,如下所示:

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;    }}

如果自己打开源码,上面会有一些注释,大致意思是按字典顺序比较两个字符串。该比较基于字符串中各个字符的 Unicode 值。按字典顺序将此 String 对象表示的字符序列与参数字符串所表示的字符序列进行比较。如果按字典顺序此 String 对象位于参数字符串之前,则比较结果为一个负整数。如果按字典顺序此 String 对象位于参数字符串之后,则比较结果为一个正整数。如果这两个字符串相等,则结果为 0;compareTo 只在方法 equals(Object) 返回 true 时才返回 0。

今天的笔记到此为止,一不小心又是凌晨了,这篇只是简单浏览了一遍,没做过多检查,如有错误,欢迎批评指正,好困,睡觉了(~﹃~)~zZ

1 0
原创粉丝点击