Gson解析动态key的JSON文件以及优化记录

先说说具体场景,当前正在做一个项目,有部分接口是通过股票代码去后台请求数据的,但是作为初入职场的小白,并没有对传入的参数进行判断,所以导致测试服务器上项目每天都是处于504状态(因为安全部门每天凌晨都会对测试服务器上的接口进行注入式攻击,以保证提前发现问题,防止线上灾难)。

于是,我得对传入的参数进行判断,由此进入正题:

现有a.json文件(以下只做模拟,具体JSON文件大小为1MB左右),文件内容为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"600000":{
"name":"股票1"
"source":"股票细节"
},
"600003":{
"name":"股票2"
"source":"股票细节"
},
"600006":{
"name":"股票3"
"source":"股票细节"
}
}

现在需要解析出上述JSON的key与请求参数做一对照。

以下列出我的解析以及优化过程。

方式一:(原始,解析时间长达4000ms!太恐怖了!)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//首先通过read方法读入json文件,以String方式返回
public static boolean read() throws IOException {
StringBuilder sb = new StringBuilder();
String str;
try (BufferedReader br = new BufferedReader(new FileReader("C:\\Users\\viruser.v-desktop\\Desktop\\a_code.json"))) {
while (null != (str = br.readLine())) {
sb.append(str);
}
br.close();
} catch (Exception e) {
System.out.println("无法在指定目录下找到名为:1.json 的文件!");
System.exit(0);
}
return sb.toString();
}

public static boolean isTrue(String code) throws IOException {
String s = read();
long startDate = System.currentTimeMillis();
JSONObject jsonObject = JSONObject.fromObject("");
Iterator<String> it = jsonObject.keys();
while (it.hasNext()){
String key = it.next();
if (key.equals(code)){
long endDate = System.currentTimeMillis();
System.out.println("耗时:"+ (endDate-startDate));
return true;
}
}
return false;
}

方式二:(运行时间稍有改观,但是平均也在3000毫秒左右)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//首先通过read方法读入json文件,以String方式返回
public static boolean read() throws IOException {
StringBuilder sb = new StringBuilder();
String str;
try (BufferedReader br = new BufferedReader(new FileReader("C:\\Users\\viruser.v-desktop\\Desktop\\a_code.json"))) {
while (null != (str = br.readLine())) {
sb.append(str);
}
br.close();
} catch (Exception e) {
System.out.println("无法在指定目录下找到名为:1.json 的文件!");
System.exit(0);
}
return sb.toString();
}

public static boolean isTrue(String code) throws IOException {
String s = read();
long startDate = System.currentTimeMillis();
Type type = new TypeToken<ArrayList<StockCodeEntity>>(){}.getType();
Gson gson = new Gson();
List<String> stringList = gson.fromJson(s,type);
System.out.println("stringList:"+stringList);
if (stringList.contains(code)){
long endDate = System.currentTimeMillis();
System.out.println("耗时:"+ (endDate-startDate));
return true;
}
return false;
}

方式三:(为什么要将文件读入完(将近2000毫秒)再拿去循环解析呢?我太笨了!)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static boolean read(String code) throws IOException {
long startDate = System.currentTimeMillis();
JsonParser parser = new JsonParser();
JsonObject json = (JsonObject) parser.parse(new FileReader("C:\\Users\\viruser.v-desktop\\Desktop\\a_code.json"));
Iterator<String> it = json.keySet();
while (it.hasNext()){
String key = it.next();
if (key.equals(code)){
long endDate = System.currentTimeMillis();
System.out.println("耗时:"+ (endDate-startDate));
return true;
}
}
}

方式四:经过上面的优化,我已经将解析时间缩小到了1000毫秒左右,但是对于一个请求过来,就光解析json就花了1秒,那也太不能接受了!所以我再次进行优化

1
2
3
4
5
6
7
8
9
10
11
12
13
public static boolean read(String code) throws IOException {
long startDate = System.currentTimeMillis();
JsonParser parser = new JsonParser();
JsonObject json = (JsonObject) parser.parse(new FileReader("C:\\Users\\viruser.v-desktop\\Desktop\\a_code.json"));
for(String key:json.keySet()){
if (key.equals(code)){
System.out.println("key:"+key);
long endDate = System.currentTimeMillis();
System.out.println("耗时:"+ (endDate-startDate));
return true;
}
}
}

经过上述优化,我已经可以将解析时间控制在400毫秒以内.但是还是不能满足要求!

方式五:目前技术极限

1
2
3
4
5
6
7
8
9
10
11
12
13
public static boolean read(String code) throws IOException {
long startDate = System.currentTimeMillis();
JsonParser parser = new JsonParser();
JsonObject json = (JsonObject) parser.parse(new FileReader("C:\\Users\\viruser.v-desktop\\Desktop\\a_code.json"));
for(Map.Entry<String, JsonElement> map : json.entrySet()){
if (map.getKey().equals(code)){
long endDate = System.currentTimeMillis();
System.out.println("耗时:"+ (endDate-startDate));
return true;
}
}
return false;
}

优化完成之后,解析时间大概在150ms左右,可以说差不多可以满足要求了,但是对于1MB的文件解析花了0.1秒,也是很不理想的!所以说留给自己提升的机会还有很大哈哈!

总结

这里顺便一提遍历Map的两种方法keySet(),entrySet()的差别。

keySet()方法返回的是key的集合set,entrySet()返回的是键值对的集合set。虽然两者从set遍历取出元素的方法是一样的,但是根据这个元素取出value的效率有些不同。前者取出的元素是key,还要去原map中遍历取出value。
后者取出的元素是键值对,直接调用getkey(),getvalue()方法就能快速取出key和value。显然在map中存在大量键值对时,使用entrySet()来取出value的效率更高。

问题:

我有点想不明白的是,我现在只需要获取键值对中的key,使用keySet()方法为什么耗费的时间要比使用entrySet()方法获取到键值对再去getKey()花费的时间还要长呢???非常奇怪!

这里参考了很多博客,对我的帮助很大

0%