Java Map容器衍生的常用Map容器分析
HashMap,HashTable,LinkedHashMap,TreeMap,ConcurrentHashMap读取,写入,安全场景测试
HashMap
最常用的Map,它根据键的HashCode 值存储数据,根据键可以直接获取它的值,具有很快的访问速度。HashMap最多只允许一条记录的键为Null(多条会覆盖);允许多条记录的值为 Null。非同步的。
TreeMap
能够把它保存的记录根据键(key)排序,默认是按升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。TreeMap不允许key的值为null。非同步的。
Hashtable(线程安全容器)
与 HashMap类似,不同的是:key和value的值均不允许为null;它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtale在写入时会比较慢。
LinkedHashMap
保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.在遍历的时候会比HashMap慢。key和value均允许为空,非同步的。
1、HashMap,HashTable,LinkedHashMap,TreeMap,ConcurrentHashMap put,get方法耗时比较
private static Integer forTimes = 10000000;
private static Integer readTimes = 10000000;
public static void hashmap(){
//map写入
long dateOne = System.currentTimeMillis();
//HashMap设置初始大小,当初始数组满足使用时,则HashMap不需要再进行扩容,减少或避免rehash次数 注:初始大小为非必须参数
HashMap<Object,Object> hashMap = new HashMap<>(10000000);
for (int i = 0; i < forTimes ; i++) {
hashMap.put(i,i);
}
long dateTwo = System.currentTimeMillis();
System.out.println("HashMap,指定初始化容器大小put使用时间:"+(dateTwo-dateOne));
//循环顺序读取
long dateThree = System.currentTimeMillis();
for (int i = 0; i <forTimes ; i++) {
hashMap.get(i);
}
long dateFour = System.currentTimeMillis();
System.out.println("HashMap,指定初始化容器大小get使用时间:"+(dateFour-dateThree));
//随机数读取
long datefive = System.currentTimeMillis();
Random random = new Random();
for (int i = 0; i < readTimes ; i++) {
hashMap.get(random.nextInt(readTimes));
}
long datesix = System.currentTimeMillis();
System.out.println("HashMap,指定初始化容器大小,随机取值使用时间:"+(datesix-datefive));
hashMap.clear();
}
每个map类型测试多次次均值
HashMap,未指定初始化容器大小put使用时间:11298 | 9652 | 9846
HashMap,未指定初始化容器大小get使用时间:1170 | 779 | 929
HashMap,未定初始化容器大小,随机取值使用时间:2641 | 2555 | 2587
Process finished with exit code 0
HashMap,指定初始化容器大小put使用时间:11241 | 10686 | 9676
HashMap,指定初始化容器大小get使用时间:1234 | 818 | 848
HashMap,指定初始化容器大小,随机取值使用时间:2713 | 2579 | 2629
Process finished with exit code 0
linkedHashMap,put使用时间:9173 | 9411 | 8446 | 10143
linkedHashMap,get使用时间:1066 | 574 | 640 | 8332
linkedHashMap,随机取值使用时间:1786 | 2148 | 2016 | 2221
Process finished with exit code 0
hashtable,put使用时间:8643 | 8204 | 12032
hashtable,get使用时间:1219 | 1132 | 1459
hashtable,随机取值使用时间:2350 | 2452 | 2387
Process finished with exit code 0
treeMap,put使用时间:15060 | 15434 | 14007
treeMap,get使用时间:1562 | 1946 | 1700
treeMap,随机取值使用时间:11087 | 11280 | 11096
Process finished with exit code 0
concurrentHashMap,put使用时间:12110 | 10941 | 11672
concurrentHashMap,get使用时间:941 | 1556 | 1584
concurrentHashMap,随机取值使用时间:2552 | 2418 | 2441
Process finished with exit code 0
- 前景:以上测试,均在main方法中,单次单个方法,执行多次得到的结果。linkedHashMap测试结果有疑义,原因还未找到——读取跳值幅度过大
- 由以上测试结果可见:
-
a.HashMap初始值的设定,对put,get耗时影响难以察觉,默认数组初始值为16,一千万次插入,扩容开销这里并未在时间上体现
-
b.单次执行,不接入业务逻辑,HashMap,linkedHashMap,HashTable,concurrentHashMap读写差别都不是很大(毫秒级)。
-
c.TreeMap写入,读取耗时明显耗时最多,随机取值时更加明显。TreeMap写入时根据Key排序(后面会贴上测试代码)
2、HashMap,HashTable,LinkedHashMap,TreeMap,ConcurrentHashMap 储存排序测试
public static void testSort(){
Random random = new Random();
HashMap<Object,Object> hashMap = new HashMap<>(20);
LinkedHashMap<Object,Object> linkedHashMap = new LinkedHashMap<>();
Hashtable<Object, Object> hashtable = new Hashtable<>();
TreeMap<Object,Object> treeMap = new TreeMap<>();
ConcurrentHashMap<Object,Object> concurrentHashMap = new ConcurrentHashMap<>();
for (int i = 0; i < 20 ; i++) {
int c = random.nextInt(100);
hashMap.put(c,i);
linkedHashMap.put(c,i);
hashtable.put(c,i);
treeMap.put(c,i);
concurrentHashMap.put(c,i);
}
System.out.println("hashMap"+hashMap);
System.out.println("linkedHashMap"+linkedHashMap);
System.out.println("hashtable"+hashtable);
System.out.println("treeMap"+treeMap);
System.out.println("concurrentHashMap"+concurrentHashMap);
}
hashMap{1=17, 66=19, 36=3, 70=1, 10=6, 43=18, 45=4, 79=16, 16=9, 81=12, 50=5, 18=15, 52=8, 57=2, 27=7, 59=11, 92=13, 95=0}
linkedHashMap{95=0, 70=1, 57=2, 36=3, 45=4, 50=5, 10=6, 27=7, 52=8, 16=9, 92=13, 59=11, 81=12, 18=15, 79=16, 1=17, 43=18, 66=19}
hashtable{92=13, 45=4, 43=18, 36=3, 81=12, 79=16, 27=7, 70=1, 66=19, 18=15, 16=9, 59=11, 10=6, 57=2, 52=8, 50=5, 1=17, 95=0}
treeMap{1=17, 10=6, 16=9, 18=15, 27=7, 36=3, 43=18, 45=4, 50=5, 52=8, 57=2, 59=11, 66=19, 70=1, 79=16, 81=12, 92=13, 95=0}
concurrentHashMap{1=17, 66=19, 36=3, 70=1, 10=6, 43=18, 45=4, 79=16, 16=9, 81=12, 50=5, 18=15, 52=8, 57=2, 27=7, 59=11, 92=13, 95=0}
Process finished with exit code 0
- 由上面测试案例可以看出
-
TreeMap存储值是按key值进行了排序,LinkedHashMap按插入顺序进行了排序,其他的Map衍生容器都是无序的
3、HashMap,HashTable,LinkedHashMap,TreeMap,ConcurrentHashMap 线程安全容器
-
HashTable,ConcurrentHashMap都是线程安全集合,在多线程中执行时,HashTable,ConcurrentHashMap的耗时对比,测试一将不再适用
-
HashMap,LinkedHashMap,TreeMap非线程安全容器,但也可以使用synchronized线程锁关键字,进行安全控制
- 线程安全这里不帖代码了,并发高时,非线程安全容器读取,写入时易发生混淆。简单说A线程写入数据,还未读取,B线程争取开销后,立即写入,A进行读取时,读取的则是被B线程覆盖后的数据。
测试一中,是单次执行单个方法测试Map衍生容器耗时。若是五个方法同时执行,执行一次改变方法执行顺序,执行的结果截然不同,耗时差异很大,这是为什么,有没有大佬解答一下。(个人猜想是涉及到类加载编译的问题????)
上一篇: 4.容器类List、ArrayList、Vector及map、HashTable、HashMap分别的区别
下一篇: 【Leetcode】378. Kth Smallest Element in a Sorted Matrix(第k个大的数)(容器)
推荐阅读
-
Java实现Map集合遍历的四种常见方式与用法分析
-
Java实现Map集合遍历的四种常见方式与用法分析
-
map接口的实现类(详解java常用工具类)
-
map接口的实现类(详解java常用工具类)
-
set容器与map容器的简单应用
-
荐 JAVA13——容器(Map接口、Equals和hashcode、Set接口、容器存储数据练习、Iterator接口)
-
C++程序员应了解的那些事(18)C++11 通过key访问map容器:下标访问、at()、find、lower_bound&upper_bound、equal_range
-
Java中常用数据结构的实现类 Collection和Map 数据结构JavaJ#算法游戏
-
Java通过工厂、Map容器创建对象的方法
-
分析Java中Map的遍历性能问题