前言
实际工作中,与C端交互的需求常会涉及到模糊查询,解决思路一般有如下几种:
- 数据库实现:like语句,全文索引;
- ES实现;
- 代码逻辑实现;
三种解决方案在针对不同场景往往能得到很好的运行效果,本文主要介绍第三种。
一、适用场景
已有的功能上忽然要加一些筛选条件,并且还要求支持模糊查询,此时如果使用数据库必然要考虑在原有表设计上加一些索引,但如果使用like语法还不能有效利用到索引,这时就可以考虑在已查出的大范围数据下使用业务逻辑来进行筛选,可以想到的筛选方法有如下几种:
- 条件字段拼接后使用indexOf()方法;
- 条件字段拼接后使用contains()方法;
- 使用List的contains()方法;
- 使用Map的containsKey()方法;
下面对四种方法一一就行实现和比较。
二、数据筛选
1.分析
四种实现方式,实现1与2可以进行模糊查询,其余两种只能进行精确匹配,用字符串实现最好使用一个分隔符进行隔开(当然最好使用一个特别的,保证不会与实际内容冲突的标识),隔开的目的是防止多个字符串拼接后得到本不应该存在的内容,如:用户查手机号为18899776600的内容,而作为筛选的字段分别有账号:2021070018899,昵称:776600CSDN,若不用分割,拼接后的内容为“2021070018899776600CSDN”,此时我们发现用户想要查找的内容出现在了拼接字符串中,所以字段分割是一定要做的。
2.实现
条件字段拼接后使用indexOf()方法:
long begin = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
StringBuilder queryStr = new StringBuilder();
queryStr.append("测试人员");
queryStr.append("-");
queryStr.append("13680001111");
queryStr.append("-");
queryStr.append("1610413816458");
queryStr.append("-");
queryStr.append("1098713809878");
int index = queryStr.indexOf("109871");
queryStr.reverse();
}
System.out.println("用时:" + (System.currentTimeMillis() - begin));
条件字段拼接后使用contains()方法:
long begin1 = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
StringBuilder queryStr = new StringBuilder();
queryStr.append("测试人员");
queryStr.append("-");
queryStr.append("13680001111");
queryStr.append("-");
queryStr.append("1610413816458");
queryStr.append("-");
queryStr.append("1098713809878");
boolean result = queryStr.toString().contains("109871");
queryStr.reverse();
}
System.out.println("用时:" + (System.currentTimeMillis() - begin1));
使用List的contains()方法:
long begin2 = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
StringBuilder queryStr = new StringBuilder();
queryStr.append("测试人员");
queryStr.append("-");
queryStr.append("13680001111");
queryStr.append("-");
queryStr.append("1610413816458");
queryStr.append("-");
queryStr.append("1098713809878");
String[] conditionArr = queryStr.toString().split("-");
List<String> conditonList = Arrays.asList(conditionArr);
boolean result = conditonList.contains("1098713809878");
queryStr.reverse();
conditonList = null;
conditionArr = null;
}
System.out.println("用时:" + (System.currentTimeMillis() - begin2));
使用Map的containsKey()方法:
long begin3 = System.currentTimeMillis();
Map<String, String> queryConditions = Maps.newHashMap();
for (int i = 0; i < 10000; i++) {
queryConditions.put("测试人员", "测试人员");
queryConditions.put("13680001111", "13680001111");
queryConditions.put("1610413816458", "1098713809878");
queryConditions.put("1098713809878", "1098713809878");
boolean result = queryConditions.containsKey("1098713809878");
queryConditions.clear();
}
System.out.println("用时:" + (System.currentTimeMillis() - begin3));
3.对比
从表中不难发现,方法1和2的性能是差不多的,因为两种方法的底层实现其实是一致的,当筛选数据达到10万的时候,使用Map实现的方式性能优势开始显现,而到100万,性能远超其他方式,不过方法1和2仍然可以保持在500ms内,正常的业务场景也是可以接受的。
处理数量 | 处理方式 | 用时(ms) |
---|---|---|
1000 | 1 | 2 |
1000 | 2 | 2 |
1000 | 3 | 6 |
1000 | 4 | 33 |
10000 | 1 | 7 |
10000 | 2 | 14 |
10000 | 3 | 20 |
10000 | 4 | 21 |
100000 | 1 | 49 |
100000 | 2 | 51 |
100000 | 3 | 96 |
100000 | 4 | 30 |
1000000 | 1 | 368 |
1000000 | 2 | 488 |
1000000 | 3 | 1073 |
1000000 | 4 | 149 |
三、总结
总而言之,并不是只要涉及到数据查询我们就要考虑数据库或者其他存储工具,在恰当的时候合理的使用内存和数据结构,也可以很好地满足当下的业务需要。