过滤字符的性能调优?
/*
*author: ahuaxuan(张荣华)
*date: 2010-05-28
*/
起因
前一段时间和其他系统集成, 另外一个系统对某个参数有一个限制,需要将字符串中的特殊字符过滤掉, 由于需要过滤的字符是对方定义的, 所以对方直接把他们系统中的过滤的代码给我了, 代码如下:
- private String escape(String s) {
- if (s == null) {
- return null;
- }
- StringBuilder sb = new StringBuilder(s.length());
- for (int i = 0; i < s.length(); i++) {
- char c = s.charAt(i);
- if (c == '\\' || c == '+' || c == '-' || c == '!' || c == '('
- || c == ')' || c == '^' || c == '[' || c == ']' || c == ':'
- || c == '{' || c == '}' || c == '~' || c == '*' || c == '?'
- || c == '|' || c == '&' || c == '>' || c == '<') {
- sb.append(“ ”);
- } else {
- sb.append(c);
- }
- }
- return sb.toString();
- }
private String escape(String s) { if (s == null) { return null; } StringBuilder sb = new StringBuilder(s.length()); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c == '\\' || c == '+' || c == '-' || c == '!' || c == '(' || c == ')' || c == '^' || c == '[' || c == ']' || c == ':' || c == '{' || c == '}' || c == '~' || c == '*' || c == '?' || c == '|' || c == '&' || c == '>' || c == '<') { sb.append(“ ”); } else { sb.append(c); } } return sb.toString(); }
拿到代码之后, ahuaxuan没有作过多的思考, 而是直接把这段代码贴到自己的代码中, 事实证明它运行”良好”.今天重新审视这段代码的时候,发现还是有改进的余地.
分析
特征分析, 上面这段代码的主要作用是将字符串中出现的固定字符替换成空格, 这些需要替换的字符也是我们常见的字符.
面对这样一个需求, 有的程序员会想到replace方法,有的程序员会想到上面的多次||的方法(但是多次||的问题在于如果你的字符不是需要过滤的字符,那么程序就一直会执行到最后一个||,这绝对性能上的浪费阿), 也有的程序员会想到hash的方式. 其实我们的最终目的其实就是:
给定一个字符, 你怎么判断这个字符是否在某个常见的字符列表中.
你肯定不想用迭代, 你也肯定不想用if else if, 你可能比较喜欢用hash, 但是注意这里的”常见字符”这几个字.
是因为它们在ascii 码里所以它们才常见, 还是因为他们常见,所以放到ascii 码里呢, 但不管怎么样, 事实上它们确实是在ascii码里, 也就是说他们之中最大的都不会超过255.
所以这个时候, 我们的方案就浮出水面了, 由于我们只要检查每个char是否==这些常见的char就行了. 说到这里, 你是不是又想起了if else if, 这个只是思维的惯性, 如果你再把这些char想像成一个个数字呢.
本质和方案
于是需求就转换成了---在个数字段中, 找到某些我们预先定义的需要过滤的数字, 并将起替换成空格,而我们定义的数字都是小于255的.
在这样一个需求中, 最快的方式是啥咧, 数组是不, 比如说我们定义的需要过滤的数字是[2, 5], 我们的数字序列是1 2 3 4 5 10 11 110......
那么我们只要遍历这个数字, 然后判断这个数字在我们的过滤数组中是否存在. 我们的过滤数组怎么组织呢?
filterchars = [0, 0, 1, 0, 0, 1],
然后我们只需要判断filterchars[k] > 1就知道是个字符是否是需要过滤的字符了(当然这里要注意数组越界了).
- public int[] dic = new int[256];
- private String filterChars = "\\+-!()^[]:{}~*?|&><";
- /**
- * User: ahuaxuan
- * Date: 2010-5-28
- * Time: 13:17:11
- */
- public CharFilter() {
- for (int k = 0; k < filterChars.length(); k++) {
- //这里浪费了一些byte.
- char c = filterChars.charAt(k);
- if (c < 256) {
- dic[c] = 1;
- }
- }
- }
- public String newEscape(String abc) {
- if (abc == null) {
- return null;
- }
- StringBuilder rs = new StringBuilder(abc.length());
- for (int k = 0; k < abc.length(); k++) {
- char c = abc.charAt(k);
- if (c < 256 && dic[c] > 0) {
- rs.append(" ");
- } else {
- rs.append(c);
- }
- }
- return rs.toString();
- }
public int[] dic = new int[256]; private String filterChars = "\\+-!()^[]:{}~*?|&><"; /** * User: ahuaxuan * Date: 2010-5-28 * Time: 13:17:11 */ public CharFilter() { for (int k = 0; k < filterChars.length(); k++) { //这里浪费了一些byte. char c = filterChars.charAt(k); if (c < 256) { dic[c] = 1; } } } public String newEscape(String abc) { if (abc == null) { return null; } StringBuilder rs = new StringBuilder(abc.length()); for (int k = 0; k < abc.length(); k++) { char c = abc.charAt(k); if (c < 256 && dic[c] > 0) { rs.append(" "); } else { rs.append(c); } } return rs.toString(); }
测试报告
下面我们来看看这段代码:
相同功能的两段代码, 运行一下测试, 测试方法如下:
- /**
- * User: ahuaxuan
- * Date: 2010-5-28
- * Time: 13:17:11
- */
- public static void main(String [] args) {
- for (int n = 0; n < 10; n++) {
- System.out.println("------------------------" + n);
- StringBuilder txt = new StringBuilder(1000);
- for (int k = 0; k < 100; k++) {
- txt.append(UUID.randomUUID().toString());
- }
- CharFilter pt = new CharFilter();
- String abc = txt.toString();
- long begin = System.currentTimeMillis();
- for (int k = 0; k < 10000; k++) {
- pt.escape(abc);
- }
- System.out.println("escape : " + (System.currentTimeMillis() - begin) + "ms");
- begin = System.currentTimeMillis();
- for (int k = 0; k < 10000; k++) {
- pt.newEscape(abc);
- }
- System.out.println("new escape : " + (System.currentTimeMillis() - begin) + "ms");
- }
- }
/** * User: ahuaxuan * Date: 2010-5-28 * Time: 13:17:11 */ public static void main(String [] args) { for (int n = 0; n < 10; n++) { System.out.println("------------------------" + n); StringBuilder txt = new StringBuilder(1000); for (int k = 0; k < 100; k++) { txt.append(UUID.randomUUID().toString()); } CharFilter pt = new CharFilter(); String abc = txt.toString(); long begin = System.currentTimeMillis(); for (int k = 0; k < 10000; k++) { pt.escape(abc); } System.out.println("escape : " + (System.currentTimeMillis() - begin) + "ms"); begin = System.currentTimeMillis(); for (int k = 0; k < 10000; k++) { pt.newEscape(abc); } System.out.println("new escape : " + (System.currentTimeMillis() - begin) + "ms"); } }
不到1k的一个文本, 每种方法调用100次, 最终得到的结果是:
escape : 103ms
new escape : 67ms
------------------------1
escape : 60ms
new escape : 42ms
------------------------2
escape : 61ms
new escape : 48ms
------------------------3
escape : 58ms
new escape : 39ms
------------------------4
escape : 56ms
new escape : 39ms
------------------------5
escape : 80ms
new escape : 39ms
------------------------6
escape : 55ms
new escape : 40ms
------------------------7
escape : 55ms
new escape : 38ms
------------------------8
escape : 55ms
new escape : 38ms
------------------------9
escape : 59ms
new escape : 41ms
如果我们把对每个不到1K的文本的过滤调用上升到10000次, 那么测试结果是:
ahuaxuan 写道
escape : 627ms
new escape : 433ms
------------------------1
escape : 564ms
new escape : 390ms
------------------------2
escape : 624ms
new escape : 390ms
------------------------3
escape : 561ms
new escape : 385ms
------------------------4
escape : 564ms
new escape : 388ms
------------------------5
escape : 600ms
new escape : 391ms
------------------------6
escape : 559ms
new escape : 382ms
------------------------7
escape : 564ms
new escape : 394ms
------------------------8
escape : 573ms
new escape : 395ms
------------------------9
escape : 566ms
new escape : 406ms
文体大小和过滤次数上升之后, 优化之后的方法比原先的方法快了近200ms.
总结:
性能就像女人的胸, 挤一挤还是有的.
当然如果你觉得文中的手段是太入门级了, 那你可以看看ahuaxuan的这篇文章:
建议继续学习:
- Xvfb+YSlow+ShowSlow搭建前端性能测试框架 (阅读:54187)
- 30分钟3300%性能提升――python+memcached网页优化小记 (阅读:12110)
- Go Reflect 性能 (阅读:9723)
- 长连接(KeepAlive)在 http 连接中的性能影响 (阅读:7029)
- SQL vs NoSQL:数据库并发写入性能比拼 (阅读:6610)
- 服务器性能测试工具推荐 (阅读:6437)
- WEB性能测试工具推荐 (阅读:5629)
- 分析进程内存分配情况,解决程序性能问题 (阅读:5327)
- 由12306.cn谈谈网站性能技术 (阅读:4910)
- [调优] Squid 不同版本的性能对比 (阅读:4163)
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:ahuaxuan 来源: 求贤若渴, 礼贤下士
- 标签: 性能 过滤
- 发布时间:2010-05-29 10:52:39
- [66] Go Reflect 性能
- [65] Oracle MTS模式下 进程地址与会话信
- [64] 如何拿下简短的域名
- [59] android 开发入门
- [59] IOS安全–浅谈关于IOS加固的几种方法
- [58] 图书馆的世界纪录
- [58] 【社会化设计】自我(self)部分――欢迎区
- [53] 视觉调整-设计师 vs. 逻辑
- [47] 界面设计速成
- [46] 读书笔记-壹百度:百度十年千倍的29条法则