HashMap和LinkedHashMap的区别

         如果不懂HashMap和LinkedHashMap的原理, 请看我以前写的2篇博客  LruCache为什么要用LinkedHashMap? 和 HashMap源码分析(JDK1.8)- 你该知道的都在这里了

          很多人说HashMap是无序的, LinkedHashMap是有序的, Why?

       直接上代码说明问题, 向HashMap对象里插入4个元素并使用iterator输出各个元素。 很多人说输出时序是无序的。但事实并非如此, HashMap是按照所在桶的下标从低到高输出的!!!  位置等于key的哈希值与0xf。 PS:示例只考虑了初始情况,即HashMap有16个桶。


一、使用iterator遍历

 //摘自HashMap.java
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("param1", "aaa");   //第14个桶
        map.put("param2", "bbb");   //第15个桶
        map.put("param3", "ccc");   //第12个桶
        map.put("param4", "ddd");   //第13个桶

        System.out.println("键值存放的位置分别是:");
        System.out.println(hash("param1")&0xf);   //14
        System.out.println(hash("param2")&0xf);   //15
        System.out.println(hash("param3")&0xf);   //12
        System.out.println(hash("param4")&0xf);   //13

        Class clz = HashMap.class;
        try {
            Field field = clz.getDeclaredField("table");
            field.setAccessible(true);
            Object obj = field.get(map);
            System.out.println("-----------------");

            Field field1 = clz.getDeclaredField("size");  //4
            field1.setAccessible(true);
            int size = (Integer)field1.get(map);
            System.out.println(size);

            Field field2 = clz.getDeclaredField("threshold");  //16*0.75=12
            field2.setAccessible(true);
            int threshold = (Integer) field2.get(map);
            System.out.println(threshold);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        //按照下标顺序输出
        for (Iterator iterator = map.values().iterator(); iterator.hasNext(); ) {
            String value = (String)iterator.next();
            System.out.println(value);
        }
    }
输出:

14
15
12
13
-----------------
4
12
ccc
ddd
aaa
bbb

      问题来了, 为什么iterator是根据下标从低到高输出呢??? 答案在HashMap.java的内部类HashIterator里。 执行iterator.next()时取table数组的值,每次执行next函数下标自增1。 所以HashMap的iterator是根据元素所在桶下标位置输出的!!!



       同样代码对比一下LinkedHashMap,默认按照插入顺序排序(也可以按照访问顺序排序)。

        Map<String, String> map = new LinkedHashMap<>();
        map.put("param1", "aaa");   //第14个桶
        map.put("param2", "bbb");   //第15个桶
        map.put("param3", "ccc");   //第12个桶
        map.put("param4", "ddd");   //第13个桶

        //按照下标顺序输出
        for (Iterator iterator = map.values().iterator(); iterator.hasNext(); ) {
            String value = (String)iterator.next();
            System.out.println(value);
        }
输出:

aaa
bbb
ccc
ddd

      LinkedHashMap是如何做到遍历时按照插入顺序输出呢?  原因在于LinkedHashMap的内部类LinkedHashIterator,执行iterator.next访问链表下一个元素, 所以可以按照插入顺序输出(即链表先后顺序)。



二、使用foreach遍历。

        Map<String, String> map = new LinkedHashMap<>();
        map.put("param1", "aaa");   //第14个桶
        map.put("param2", "bbb");   //第15个桶
        map.put("param3", "ccc");   //第12个桶
        map.put("param4", "ddd");   //第13个桶

        //Java8新增的遍历方式
        map.forEach((key, value) -> {
            System.out.println("key:" + key + ", value:" + value);
        });
输出:

key:param1, value:aaa
key:param2, value:bbb
key:param3, value:ccc
key:param4, value:ddd
      LinkedHashMap仍然按照插入顺序输出, 原因在源码里的forEach函数, 循环读取after对象。



         将示例代码中的LinkedHashMap改为HashMap,遍历时序仍然按照桶位置输出。

        Map<String, String> map = new HashMap<>();
        map.put("param1", "aaa");   //第14个桶
        map.put("param2", "bbb");   //第15个桶
        map.put("param3", "ccc");   //第12个桶
        map.put("param4", "ddd");   //第13个桶

        //Java8新增的遍历方式
        map.forEach((key, value) -> {
            System.out.println("key:" + key + ", value:" + value);
        });
输出:

key:param3, value:ccc
key:param4, value:ddd
key:param1, value:aaa
key:param2, value:bbb



综上所述:

        HashMap的无序其实也有迹可循, 即按照桶下标先后排序;如果有哈希碰撞的情况,则同一个桶位置按照链表先后顺序输出。

        LinkedHashMap的有序是因为维护了双向链表。








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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值