今天和大家一起测试下各种常见 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 {
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;
}
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.添加
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.获取
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.移除
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.遍历
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.测试结果汇总
性能分别按高、中、低表示:
测试项目 | TreeMap | HashMap | Hashtable | LinkedHashMap | IdentityHashMap | WeakHashMap |
---|
添加 | 中 | 高 | 高 | 低 | 中 | 高 | 获取 | 低 | 中 | 中 | 中 | 高 | 中 | 移除 | 低 | 中 | 中 | 中 | 高 | 高 | 遍历 | 低 | 高 | 高 | 高 | 中 | 中 |
小结 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;
本次分享至此结束,希望本文对你有所帮助,若能点亮下方的点赞按钮,在下感激不尽,谢谢您的【精神支持】。
若有任何疑问,也欢迎与我交流,若存在不足之处,也欢迎各位指正!
|