一、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)
总结一下:
LinkedHashMap的特点如下:
- LinkedHashMap是继承于HashMap,是基于HashMap和双向链表来实现的。
- HashMap无序;LinkedHashMap有序,可分为插入顺序和访问顺序两种。如果是访问顺序,那put和get操作已存在的Entry时,都会把Entry移动到双向链表的表尾(其实是先删除再插入)。
- LinkedHashMap存取数据,还是跟HashMap一样使用的Entry[]的方式,双向链表只是为了保证顺序。
- LinkedHashMap是线程不安全的。
LinkedHashMap的使用场景:
- LinkedHashMap可以实现LRU算法