使用HashMap集合存取元素,产生顺序不一致的原因(以及两种可以实现顺序存取的方法)

相信大家在工作学习的过程中,都有遇到过这样的情况:那就是使用hashmap类型的集合时,取出元素的顺序和放入元素的顺序不一致,后面一上网搜索,发现将HashMap换成LinkedHashMap就解决了元素存取的无序性问题。不知道大家有没有考虑到为什么hashmap的存取顺序会不一致?以及是否还有别的方法来实现顺序读取?

下面就针对上诉的两个问题进行探讨一下。

首先,HashMap的存取为什么不一致?这主要是因为Hashmap的put和get方法。

(以jdk1.8版本为例,hashmap的底层结构为table数组+entry链表/红黑树)

put()方法的大致流程如下(省略了扩容的部分,有兴趣了解的可以自行搜索):

1、对要put的元素key值的hashCode做hash运算,得到的index值就是数组下标

2、判断该位置是否存在元素

2.1、如果没有则将元素放到此位置上

2.2、如果已经存在元素,说明出现了hash冲突,这时候需要遍历该位置的链表元素,使用equals()方法将当前元素的key和链表元素的key一一进行比较,看看是否有相等的key

2.2.1、如果有相等的key,则将新的value替换掉旧的value

2.2.2、如果没有找到相等的key,则在链表的最前面加入当前节点

3、如果链表过长,链表就会转为红黑树

再来说get()方法:

1、对key值的hashCode做hash运算,得到数组下标index

2、根据index位置去找元素,如果该位置上只有一个元素,则直接返回

3、如果index位置的元素是以链表或者红黑树形式存在的,则需要使用equal()方法遍历比较,找到相等key的元素并返回。

通过对put方法和get方法的实现过程进行分析后,不难发现造成元素读取不一致的原因。

那就是,hashmap取元素,都是先拿到元素key值的hashCode,再做hash运算,得到实际存放的下标,再根据下标去取出的,而这个和元素的放入顺序是无关的。

下面结合代码来证明。

 初始化两个元素相同的数组,只是一个升序,一个降序。依次放入hashmap集合,再依次取出,比较这两个数组的读取结果是否和存放相关。

结果如下: 

 可以看出,只要是同一组数据,不管以什么顺序放入,读取的顺序都是固定的。

那如果要以指定的顺序拿到map集合的元素,该怎么做?

方法一使用LinkedHashMap代替HashMap,LinkedHashMap读取元素的顺序与存放元素的顺序保持一致代码实测结果如下图:

 方法二:将HahMap转为List集合,也就是构建一个元素为键值对(Map.Entry)的List集合,再搭配Collection.sort()方法对集合元素自定义排序(需要重写compare()方法)

 结果如下,可以看到也实现了顺序读取hashMap元素的功能

 

完整代码如下

import java.util.*;

public class HashMapOrder {
    //初始化一个数组
    public int[] initArray(int num)
    {
        int[] arr = new int[num];
        for(int i=0;i<num;i++)
        {
            arr[i]=i+1;
        }
        return arr;
    }

    //按顺序将数组的值放入Map类型的集合中
    public Map<String,Integer> setOrderMap(int[] arr,Map map,int order) {

        String mapType = map.getClass().getTypeName();
        System.out.println("map类型为"+mapType+",写入Map集合的顺序如下:");
        if (order == 1) {
            for (int i = 0; i < arr.length; i++) {
                map.put("第" + (i + 1) + "位:", arr[i]);
                System.out.println("第" + (i + 1) + "位:" + arr[i]);
            }
        }
            else{
                for (int i = arr.length-1; i>=0; i--) {
                    map.put("第" + (i + 1) + "位:", arr[i]);
                    System.out.println("第" + (i + 1) + "位:" + arr[i]);
                }
            }
            return map;

    }


    //从HashMap类型的集合里读取元素
    public void getOrderMap(Map<String,Integer> map)
    {

        System.out.println("从map集合取出的元素如下:");
        for(Map.Entry<String,Integer> entry:map.entrySet())
        {

            System.out.println(entry.getKey() + entry.getValue());
        }
    }


    //对hashmap的元素进行排序
    public void sortHashMap(Map<String,Integer> map)
    {
        List<Map.Entry<String,Integer>> list =  new ArrayList<Map.Entry<String,Integer>>(map.entrySet());
        Collections.sort(list,new Comparator<Map.Entry<String,Integer>>(){

            @Override
            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                return o1.getValue().compareTo(o2.getValue());
            }
        });


        System.out.println("对hashmap里的元素手动排序后取出的顺序:" );
        for(Map.Entry<String,Integer> entry:list)
        {

            System.out.println(entry.getKey() + entry.getValue());
        }
    }

    public static void main(String[] args) {
        HashMapOrder hashMapOrder = new HashMapOrder();
        int[] arr = hashMapOrder.initArray(10);
        Map mapType1 = new HashMap();
        Map mapType2 = new LinkedHashMap();
        //定义排序的规则,正数为升序,否则为降序
        int order = 1;
        //升序排列,hashmap类型
        Map<String,Integer> map = hashMapOrder.setOrderMap(arr,mapType1,order);


        hashMapOrder.getOrderMap(map);

        System.out.println("--------------分割线---------------");
        order = -1;

        //降序排列,hashmap类型
         map = hashMapOrder.setOrderMap(arr,mapType1,order);
         hashMapOrder.getOrderMap(map);

         //对hashmap手动排序
        System.out.println("--------------分割线---------------");
        map = hashMapOrder.setOrderMap(arr,mapType1,order);
        hashMapOrder.sortHashMap(map);

//        hashMapOrder.getOrderMap(map);


        System.out.println("--------------分割线---------------");
        //升序排列,LinkedHashMap类型
        map = hashMapOrder.setOrderMap(arr,mapType2,order);
        hashMapOrder.getOrderMap(map);
    }
}

写在最后如果这些文章对您有所帮助,不妨点个赞支持一下,谢谢!

如果有写的不对的地方,也欢迎大家批评指正。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值