最近工作中遇到某些特定的需求,创建一个路由表Map,当把一堆路由信息key-value塞到一个HashMap,并加载到jvm中。同时逻辑代码中有大量的操作,需要去遍历这个Map。传统的HashMap会随着HashMap的大小增大,效率变得越来越低。所以对HashMap做了一些小改造。
原理很简单,在原有的HashMap前,增加一次hash,缩小遍历范围。
适用于初始化一次,就不怎么更改,只读不常写的场景,像固定加载到jvm中的路由数据、配置等。
Demo:
//map数量
int mapCount = 500;
//遍历次数
int iteratorTimes = 1000000;
//定义变种HashMap hash槽
int hashCao = 10;
Random random = new Random();
//原有的HashMap 初始化 500个key-value
Map<String, String> normalMap = new HashMap<>();
for (int i = 0; i < mapCount; i++) {
normalMap.put(String.valueOf(i), "我是" + String.valueOf(i));
}
//记录开始时间 遍历指定次数
long startTime = System.currentTimeMillis();
for (int x =0;x<iteratorTimes;x++) {
String randomNum = String.valueOf(random.nextInt(mapCount));
//System.out.println("随机数为:" + randomNum);
for (Iterator<Map.Entry<String, String>> iterator = normalMap.entrySet().iterator(); iterator.hasNext(); ) {
Map.Entry<String, String> entry = iterator.next();
//模拟命中
if (randomNum.equals(entry.getKey())) {
//打印命中key-value
//System.out.println("第" + entry.getKey() + "个:" + entry.getValue());
}
}
}
//普通HashMap结束
long endTime = System.currentTimeMillis();
//构造变种HashMap 外层map的key为自定义hash槽
Map<Integer, Map<String,String>> fasterMap = new HashMap<>();
//定义指定数量hash槽
for (int j = 0; j < hashCao; j++) {
fasterMap.put(j, new HashMap<>());
}
//按取模方式模拟 取出相应槽位的HashMap,再将数据塞入。
for (int i = 0; i < mapCount; i++) {
fasterMap.get(i % hashCao).put(String.valueOf(i), "我是" + String.valueOf(i));
}
//记录变种HashMap 遍历开始时间
long startTime2 = System.currentTimeMillis();
//遍历指定次数
for (int x = 0; x < iteratorTimes; x++) {
int randInt = random.nextInt(mapCount);
//每次遍历 用随机数按hash槽数取模来模拟,取出相应槽位hashMap
Map<String, String> curMap = fasterMap.get(randInt % hashCao);
String randomNum = String.valueOf(randInt);
//打印生成的随机数
//System.out.println("随机数为:" + randomNum);
//遍历相应槽位HashMap
for (Iterator<Map.Entry<String, String>> iterator = curMap.entrySet().iterator(); iterator.hasNext(); ) {
Map.Entry<String, String> entry = iterator.next();
if (randomNum.equals(entry.getKey())) {
//模拟命中
//System.out.println("第" + entry.getKey() + "个:" + entry.getValue());
}
}
}
//变种HashMap结束
long endTime2 = System.currentTimeMillis();
System.out.println("normalMap耗时:" + (endTime - startTime));
System.out.println("fasterMap耗时:" + (endTime2 - startTime2));
给出几组数据结果(每种情况由于Random随机数的不稳定因素,每次结果可能会不一样,但不影响大致趋势)
map中key-value数量/个 | hash槽数/个 | 遍历次数/次 | 普通hashMap耗时/ms | 变种fasterMap 耗时/ms |
---|---|---|---|---|
5 | 10 | 1000,000 | 296 | 224 |
10 | 10 | 1000,000 | 248 | 240 |
100 | 10 | 1000,000 | 1410 | 206 |
100 | 100 | 1000,000 | 1425 | 257 |
500 | 10 | 1000,000 | 4634 | 519 |
500 | 100 | 1000,000 | 4107 | 177 |
1000 | 10 | 1000,000 | 7872 | 866 |
1000 | 100 | 1000,000 | 7832 | 141 |
相对来说,是一种空间换时间的做法,特定场景下还算可观,文中取模的步骤可以根据需求替换成hash等等一些算法。
网友评论