欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java Map容器衍生的常用Map容器分析

程序员文章站 2022-03-04 14:30:21
...

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衍生容器耗时。若是五个方法同时执行,执行一次改变方法执行顺序,执行的结果截然不同,耗时差异很大,这是为什么,有没有大佬解答一下。(个人猜想是涉及到类加载编译的问题????)

相关标签: 容器