HashMap存入和取出数据顺序不一致

一、HashMap 乱序问题介绍

HashMap 是我们在开发中常用的Map数据结构,它根据 HashCode的值存储数据,根据键进行取值,具有很快的访问速度。

关于HashMap的缺点我们知道它是线程不安全的,这里我们可以通过Collections.synchronizedMap()方法或者使用ConcurrentHashMap来解决同步问题。

这里要指出来的一点是,HashMap存取是乱序的,下面我们通过一个实例看一下:

public class HashMapTest {
    public static void main(String[] args) {
        Map<String, String> hashMap = new HashMap<String, String>();
        hashMap.put("key0", "value0");
        hashMap.put("key1", "value1");
        hashMap.put("key2", "value2");
        Set<Map.Entry<String, String>> set = hashMap.entrySet();
        for (Map.Entry entry : set) {
            String key = (String) entry.getKey();
            String value = (String) entry.getValue();
            System.out.println("key:" + key + ",value:" + value);
        }
    }
}

输出结果:

key:key1,value:value1
key:key2,value:value2
key:key0,value:value0

可以看到,我们是按照xxx1、xxx2、xxx3的顺序插入的,但是输出结果并不是按照顺序的。

此问题在实际开发中可能存在的场景:Map作为容器提交请求参数,但是如果使用HashMap进行存储,则在用于上传图片的时候,会出现图片乱序的问题。

二、解决HashMap乱序问题的方案

解决方案为:使用LinkedHashMap

public static void main(String[] args) {
    Map<String, String> hashMap = new LinkedHashMap<>();
    hashMap.put("key0", "value0");
    hashMap.put("key1", "value1");
    hashMap.put("key2", "value2");
    Set<Map.Entry<String, String>> set = hashMap.entrySet();
    for (Map.Entry entry : set) {
        String key = (String) entry.getKey();
        String value = (String) entry.getValue();
        System.out.println("key:" + key + ",value:" + value);
    }
}

输出结果:

key:key0,value:value0
key:key1,value:value1
key:key2,value:value2

三、LinkedHashMap 说明

LinkedHashMap 是HashMap的一个子类,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的。

LinkedHashMap继承自HashMap,是基于HashMap的基础之上进行的扩展:

public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V> {

构造函数基本上也是super的HashMap的构造函数,这里特别说明一下其中一个构造函数:

 public LinkedHashMap(int initialCapacity,  float loadFactor, boolean accessOrder) {
     super(initialCapacity, loadFactor);
     this.accessOrder = accessOrder;
}

前面两个参数 initialCapacity 和 loadFactor 都是比较熟悉的,分别为初始容量和负载因子。这里我们着重说一下 accessOrder,如果将accessOrder设置为false,表示不是访问顺序而是插入顺序存储的,这也是默认值,表示LinkedHashMap中存储的顺序是按照调用put方法插入的顺序进行排序的。这表示LinkedHashMap存储顺序有两种:插入顺序 和 访问顺序。

以下是两篇很好的文章,帮助大家基于源码理解LinkedHashMap:

1. LinkedHashMap 源码详细分析(JDK1.8)

2. 图解LinkedHashMap原理(JDK1.7)

总结一下:

LinkedHashMap的特点如下:

  • LinkedHashMap是继承于HashMap,是基于HashMap和双向链表来实现的。
  • HashMap无序;LinkedHashMap有序,可分为插入顺序和访问顺序两种。如果是访问顺序,那put和get操作已存在的Entry时,都会把Entry移动到双向链表的表尾(其实是先删除再插入)。
  • LinkedHashMap存取数据,还是跟HashMap一样使用的Entry[]的方式,双向链表只是为了保证顺序。
  • LinkedHashMap是线程不安全的。

LinkedHashMap的使用场景:

  • LinkedHashMap可以实现LRU算法

 

 

原文地址:https://www.cnblogs.com/renhui/p/12145581.html