各种类型的Map性能如何? --《JAVA编程思想》73

今天和大家一起测试下各种常见 Map 类型在不同操作场景下的性能。

0. 准备工作

制定一个测试模板类,通过泛型兼容各种 Map 类型;定义抽象方法 test() ,用于执行各项测试操作,返回值为测试所花费的时间。

public abstract class MapTest<T extends Map> {

    private T map;

    public abstract long test();

    public MapTest(T map) {
        this.map = map;
    }

    public T getMap() {
        return map;
    }

    public void setMap(T map) {
        this.map = map;
    }
    
}

再准备一个用于执行测试方法的工具类 MapPerformanceTest ,将后续待各项测试方法编写在其中,工具类提供构造测试数据的 getMap() 方法和打印测试信息的 displayHead() 方法。

public class MapPerformanceTest {

    /**
     * 获取指定长度的map
     *
     * @param map  容器
     * @param size 容器长度
     * @param <T>
     * @return
     */
    public static <T extends Map> T getMap(T map, int size) {
        //清空遗留数据
        map.clear();
        for (int i = 0; i < size; i++) {
            map.put(i, i);
        }
        return map;
    }

    /**
     * 打印测试测试
     *
     * @param t          容器
     * @param methodName 测试项目
     * @param <T>
     */
    public static <T> void displayHead(T t, String methodName) {
        System.out.println();
        System.out.println(t.getClass().getSimpleName() + "\t" + methodName);
        System.out.println("容器长度\t操作次数\t平均时间");
    }

}

1.添加

    /**
     * 添加测试
     *
     * @param map  容器
     * @param size 容器长度
     * @param <T>
     */
    public static <T extends Map> void put(T map, int size) {
        MapTest<T> mapTest = new MapTest<T>(map) {
            @Override
            public long test() {
                long startTime = System.nanoTime();
                //复用公共方法
                MapPerformanceTest.getMap(getMap(), size);
                long endTime = System.nanoTime();
                return endTime - startTime;
            }
        };
        //操作时间
        long operateTime = mapTest.test();
        //操作次数
        int count = size;
        //平均操作时间
        long avg = operateTime / count;
        System.out.println(size + "\t" + count + "\t" + avg);
    }

    public static void main(String[] args) {
        Map[] maps = {new TreeMap<Integer, Integer>(), new HashMap(),
                new Hashtable(), new LinkedHashMap(),
                new IdentityHashMap(),
                new WeakHashMap()};
        for (Map map : maps) {
            displayHead(map, "put");
            //使用移位操作符计算操作次数,增强代码可读性
            put(map, 10 << 10);
            put(map, 10 << 12);
            put(map, 10 << 14);
        }
    }
TreeMap	put
容器长度	操作次数	平均时间
10240	10240	791
40960	40960	290
163840	163840	141

HashMap	put
容器长度	操作次数	平均时间
10240	10240	201
40960	40960	103
163840	163840	58

Hashtable	put
容器长度	操作次数	平均时间
10240	10240	228
40960	40960	138
163840	163840	66

LinkedHashMap	put
容器长度	操作次数	平均时间
10240	10240	275
40960	40960	133
163840	163840	243

IdentityHashMap	put
容器长度	操作次数	平均时间
10240	10240	253
40960	40960	216
163840	163840	105

WeakHashMap	put
容器长度	操作次数	平均时间
10240	10240	305
40960	40960	81
163840	163840	57

2.获取

    /**
     * 获取测试
     * @param map 容器
     * @param loops 循环次数
     * @param size 容器长度
     * @param <T>
     */
    public static <T extends Map> void get(T map, int loops, int size) {
        MapTest<T> mapTest = new MapTest<T>(map) {
            @Override
            public long test() {
                long startTime = System.nanoTime();
                //指定随机因子,保证每次运行获得的随机数都是一致的
                Random random = new Random(47);
                for (int i = 0; i < loops; i++) {
                    map.get(random.nextInt(size));
                }
                long endTime = System.nanoTime();
                return endTime - startTime;
            }
        };
        //操作时间
        long operateTime = mapTest.test();
        //操作次数
        int count = loops;
        //平均操作时间
        long avg = operateTime / count;
        System.out.println(size + "\t" + count + "\t" + avg);
    }

    public static void main(String[] args) {
        Map[] maps = {new TreeMap<Integer, Integer>(), new HashMap(),
                new Hashtable(), new LinkedHashMap(),
                new IdentityHashMap(),
                new WeakHashMap()};
        Integer[][] testParams = {{10 << 7, 10 << 10}, {10 << 8, 10 << 12}, {10 << 9, 10 << 14}};
        for (Map map : maps) {
            displayHead(map, "get");
            for (int i = 0; i < testParams.length; i++) {
                int loops = testParams[i][0];
                int size = testParams[i][1];
                map = getMap(map, size);
                get(map, loops, size);
            }
        }
    }
TreeMap	get
容器长度	操作次数	平均时间
10240	1280	1468
40960	2560	521
163840	5120	841

HashMap	get
容器长度	操作次数	平均时间
10240	1280	387
40960	2560	163
163840	5120	238

Hashtable	get
容器长度	操作次数	平均时间
10240	1280	188
40960	2560	114
163840	5120	288

LinkedHashMap	get
容器长度	操作次数	平均时间
10240	1280	173
40960	2560	265
163840	5120	303

IdentityHashMap	get
容器长度	操作次数	平均时间
10240	1280	357
40960	2560	140
163840	5120	146

WeakHashMap	get
容器长度	操作次数	平均时间
10240	1280	343
40960	2560	168
163840	5120	200

3.移除

    /**
     * 移除测试
     *
     * @param map   容器
     * @param loops 循环次数
     * @param size  容器长度
     * @param <T>
     */
    public static <T extends Map> void remove(T map, int loops, int size) {
        MapTest<T> mapTest = new MapTest<T>(map) {
            @Override
            public long test() {
                long startTime = System.nanoTime();
                //指定随机因子,保证每次运行获得的随机数都是一致的
                Random random = new Random(47);
                for (int i = 0; i < loops; i++) {
                    map.remove(random.nextInt(size - loops));
                }
                long endTime = System.nanoTime();
                return endTime - startTime;
            }
        };
        //操作时间
        long operateTime = mapTest.test();
        //操作次数
        int count = loops;
        //平均操作时间
        long avg = operateTime / count;
        System.out.println(size + "\t" + count + "\t" + avg);
    }


    public static void main(String[] args) {
        Map[] maps = {new TreeMap<Integer, Integer>(), new HashMap(),
                new Hashtable(), new LinkedHashMap(),
                new IdentityHashMap(),
                new WeakHashMap()};
        Integer[][] testParams = {{10 << 7, 10 << 10}, {10 << 8, 10 << 12}, {10 << 9, 10 << 14}};
        for (Map map : maps) {
            displayHead(map, "remove");
            for (int i = 0; i < testParams.length; i++) {
                int loops = testParams[i][0];
                int size = testParams[i][1];
                map = getMap(map, size);
                remove(map, loops, size);
            }
        }
    }
TreeMap	remove
容器长度	操作次数	平均时间
10240	1280	3142
40960	2560	745
163840	5120	1172

HashMap	remove
容器长度	操作次数	平均时间
10240	1280	366
40960	2560	206
163840	5120	335

Hashtable	remove
容器长度	操作次数	平均时间
10240	1280	241
40960	2560	144
163840	5120	294

LinkedHashMap	remove
容器长度	操作次数	平均时间
10240	1280	518
40960	2560	163
163840	5120	302

IdentityHashMap	remove
容器长度	操作次数	平均时间
10240	1280	323
40960	2560	150
163840	5120	151

WeakHashMap	remove
容器长度	操作次数	平均时间
10240	1280	432
40960	2560	157
163840	5120	217

4.遍历

    /**
     * 遍历测试
     *
     * @param map  容器
     * @param size 容器长度
     * @param <T>
     */
    public static <T extends Map> void iter(T map, int size) {
        MapTest<T> mapTest = new MapTest<T>(map) {
            @Override
            public long test() {
                long startTime = System.nanoTime();
                Set keySet = map.keySet();
                for (Object key : keySet) {
                    map.get(key);
                }
                long endTime = System.nanoTime();
                return endTime - startTime;
            }
        };
        //操作时间
        long operateTime = mapTest.test();
        //操作次数
        int count = size;
        //平均操作时间
        long avg = operateTime / count;
        System.out.println(size + "\t" + count + "\t" + avg);
    }

    public static void main(String[] args) {
        Map[] maps = {new TreeMap<Integer, Integer>(), new HashMap(),
                new Hashtable(), new LinkedHashMap(),
                new IdentityHashMap(),
                new WeakHashMap()};
        Integer[] testParams = {10 << 10, 10 << 12, 10 << 14};
        for (Map map : maps) {
            displayHead(map, "iter");
            for (int i = 0; i < testParams.length; i++) {
                int size = testParams[i];
                map = getMap(map, size);
                iter(map, size);
            }
        }
    }
TreeMap	iter
容器长度	操作次数	平均时间
10240	10240	415
40960	40960	166
163840	163840	89

HashMap	iter
容器长度	操作次数	平均时间
10240	10240	259
40960	40960	41
163840	163840	20

Hashtable	iter
容器长度	操作次数	平均时间
10240	10240	276
40960	40960	99
163840	163840	28

LinkedHashMap	iter
容器长度	操作次数	平均时间
10240	10240	159
40960	40960	11
163840	163840	15

IdentityHashMap	iter
容器长度	操作次数	平均时间
10240	10240	178
40960	40960	38
163840	163840	66

WeakHashMap	iter
容器长度	操作次数	平均时间
10240	10240	225
40960	40960	58
163840	163840	50

5.测试结果汇总

性能分别按高、中、低表示:

测试项目TreeMapHashMapHashtableLinkedHashMapIdentityHashMapWeakHashMap
添加
获取
移除
遍历

小结
1.Map 中需要频繁添加时,优先选择 HashMap 或者 Hashtable ,Hashtable 中所有的操作都是同步的,所以它是线程安全的,而 HaspMap 不是,故 HaspMap 的性能略强于 Hashtable,再不考虑线程安全的前提下,更推荐使用 HashMap ;WeakHashMap 虽然添加性能高,但其存储的 key 都是弱引用,当 key 值的引用被置为 null时,会自动被垃圾回收,一般用于做缓存,大家可以根据需求酌情选择;

2.在随机获取方面,IdentityHashMap 的性能表现最为优异,但其实根据 “==” 来判断 key 值是否重复,而一般的 Map 都是通过 equals() 和 hashCode() 来判断;

3.在随机移除方面,IdentityHashMap 和 WeakHashMap 的性能最高;

4.HashMap、Hashtable、LinkedHashMap 在遍历方面性能都很优异,如果你对插入顺序有要求,建议使用底层为链表结构的 LinkedHashMap ,其他两个无法保证插入顺序;

5.TreeMap 虽然各方面性能都不佳,但它可以控制 key 的排序,这是其他 Map 类无法替代的;

6.HashMap 一般是我们的首选,各方面性能都不错,如果有特定业务场景再考虑使用其他 Map;


本次分享至此结束,希望本文对你有所帮助,若能点亮下方的点赞按钮,在下感激不尽,谢谢您的【精神支持】。

若有任何疑问,也欢迎与我交流,若存在不足之处,也欢迎各位指正!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BaymaxCS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值