十分钟探讨map与hashmap的排序

今天遇到一个关于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=3
b=1
cds=1
daaa=21
daac=23
daaz=25
e=22
f=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=3
daaz=25
daac=23
e=22
daaa=21
f=2
b=1
cds=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

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值