锁定老帖子 主题:实践中的重构11_茴香豆的多种写法
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2010-12-16
最后修改:2011-06-06
/** * 这个是最早的程序版本,把一个字符串转换成一个16进制编码的字符串。 * 看到这段代码的时候,第一反应是怎么不用StringBuilder。而是字符串相加。 * 其次,这个是一个常用的工具类,每天的调用量很大,性能看上去不怎么好。 * */ public static String str2HexString_0(String str) { String ret = ""; byte[] b = str.getBytes(); for (int i = 0; i < b.length; i++) { String hex = Integer.toHexString(b[i] & 0xFF); if (hex.length() == 1) { hex = '0' + hex; } ret += hex.toUpperCase(); } return ret; } /** * 第一次尝试修改。核心思想是用StringBuilder代替String。展开数字对String的转换,避免调用toUpperCase方法。 * */ public static String str2HexString_1(String str) { StringBuilder sb = new StringBuilder(str.length() << 1); byte[] data = str.getBytes(); for (int i = 0; i < data.length; i++) { int left = (data[i] & 0xF0) >> 4; append(sb, left); int right = data[i] & 0x0F; append(sb, right); } return sb.toString(); } private static void append(StringBuilder sb, int i) { switch (i) { case 0: sb.append("0"); break; case 1: sb.append("1"); break; case 2: sb.append("2"); break; case 3: sb.append("3"); break; case 4: sb.append("4"); break; case 5: sb.append("5"); break; case 6: sb.append("6"); break; case 7: sb.append("7"); break; case 8: sb.append("8"); break; case 9: sb.append("9"); break; case 10: sb.append("A"); break; case 11: sb.append("B"); break; case 12: sb.append("C"); break; case 13: sb.append("D"); break; case 14: sb.append("E"); break; case 15: sb.append("F"); break; default: break; } } /** * 测试发现,第一次的修改速度提高了。但是code变长了,看起来不是很舒服。 想了想不用switch是可以提高code的简洁程度的。 * 于是有了第2次修改。 * */ public static String str2HexString_2(String str) { StringBuilder sb = new StringBuilder(str.length() << 1); byte[] data = str.getBytes(); for (int i = 0; i < data.length; i++) { int left = (data[i] & 0xF0) >> 4; sb.append("0123456789ABCDEF".charAt(left)); int right = data[i] & 0x0F; sb.append("0123456789ABCDEF".charAt(right)); } return sb.toString(); } /** 想了想用数组不用charAt会不会更快呢。于是有了下面的版本。 */ public static String str2HexString_3(String str) { StringBuilder sb = new StringBuilder(str.length() << 1); byte[] data = str.getBytes(); for (int i = 0; i < data.length; i++) { int left = (data[i] & 0xF0) >> 4; sb.append(digit2string[left]); int right = data[i] & 0x0F; sb.append(digit2string[right]); } return sb.toString(); } private static final String[] digit2string = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", }; /** * 测试发现第3次修改的性能不怎么样。而且code不易读了。算是一个失败的修改。 * 出去抽了一根烟,突然醒悟到为什么一定要用StringBuilder呢, * 这个程序实质上就是一个对数据的操作。既然是对数据的操作,那就直接操作数据好了。干嘛要多绕一圈呢。 于是有了下面的版本。 * */ public static String str2HexString_4(String str) { byte[] oldData = str.getBytes(); char[] tmpData = new char[oldData.length << 1]; for (int i = 0; i < oldData.length; i++) { int left = (oldData[i] & 0xF0) >> 4; tmpData[i << 1] = "0123456789ABCDEF".charAt(left); int right = oldData[i] & 0x0F; tmpData[(i << 1) + 1] = "0123456789ABCDEF".charAt(right); } return new String(tmpData); } /** 网友gdpglc的思路.*/ public static String str2HexString_5(String str) { byte[] oldData = str.getBytes(); char[] tmpData = new char[oldData.length << 1]; for (int i = 0; i < oldData.length; i++) { int left = (oldData[i] & 0xF0) >> 4; tmpData[i << 1] = digit2char[left]; int right = oldData[i] & 0x0F; tmpData[(i << 1) + 1] = digit2char[right]; } return new String(tmpData); } private static final char[] digit2char = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', }; /** * 返回一个随机的字符串。150是基于该程序使用场景的抽样得到的长度。 * */ private static String getRandomString() { StringBuilder sb = new StringBuilder(); Random r = new Random(); int length = 150 + r.nextInt(50); for (int i = 0; i < length; i++) { sb.append('a' + r.nextInt(26)); } return sb.toString(); } /** * 随机单元测试。 结果见perfResult.txt。 * * */ @Test public void test() { for (int i = 0; i < 100; i++) { String inputString = getRandomString(); String result_0 = str2HexString_0(inputString); String result_1 = str2HexString_1(inputString); String result_2 = str2HexString_2(inputString); String result_3 = str2HexString_3(inputString); String result_4 = str2HexString_4(inputString); String result_5 = str2HexString_5(inputString); Assert.assertEquals(result_0, result_1); Assert.assertEquals(result_1, result_2); Assert.assertEquals(result_2, result_3); Assert.assertEquals(result_3, result_4); Assert.assertEquals(result_4, result_5); Assert.assertEquals(result_5, result_0); } } 性能结果报告: 100000次执行的性能结果。 单位ms。 str2HexString_0 132016 str2HexString_1 8906 str2HexString_2 5297 str2HexString_3 7859 str2HexString_4 4203 str2HexString_5 3985 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-12-16
很不错,看不出什么继续优化的地方了
|
|
返回顶楼 | |
发表时间:2010-12-17
效率肯定高了但是可读性基本上没有任何好转。
|
|
返回顶楼 | |
发表时间:2010-12-17
"0123456789ABCDEF".charAt(left)
地方还可以优化 char[] chs={'0','1'........'F'} chs[left] 不过我一定不会这么用。我宁可用Character.forDigit方法,这样的代码可读性太差了。这么在乎性能的理由是什么?你在写系统软件吗? 把乘2写成<<1,唉。我觉得真是精力过剩了.......... 这哪能叫重构.... 整个一个反重构 |
|
返回顶楼 | |
发表时间:2010-12-17
这好用吗??
|
|
返回顶楼 | |
发表时间:2010-12-17
gdpglc 写道 "0123456789ABCDEF".charAt(left)
地方还可以优化 char[] chs={'0','1'........'F'} chs[left] 不过我一定不会这么用。我宁可用Character.forDigit方法,这样的代码可读性太差了。这么在乎性能的理由是什么?你在写系统软件吗? 把乘2写成<<1,唉。我觉得真是精力过剩了.......... 这哪能叫重构.... 整个一个反重构 你的改法和我的方法3没有本质区别,但是性能结果不理想。 这个过程也许有一点点过。但是反重构我是不同意的。 软件本身就不是一个简单的事情。看到<<或者>>如果你觉得不舒服我只能说个人的理解不一样。 这个类一看就知道应该是底层协议用的,当然是比较底层的。 |
|
返回顶楼 | |
发表时间:2010-12-17
这个类还有什么用处?还可以用来在字符编码间转码吧?
|
|
返回顶楼 | |
发表时间:2010-12-17
最后修改:2010-12-17
zhang_xzhi_xjtu 写道 gdpglc 写道 "0123456789ABCDEF".charAt(left)
地方还可以优化 char[] chs={'0','1'........'F'} chs[left] 不过我一定不会这么用。我宁可用Character.forDigit方法,这样的代码可读性太差了。这么在乎性能的理由是什么?你在写系统软件吗? 把乘2写成<<1,唉。我觉得真是精力过剩了.......... 这哪能叫重构.... 整个一个反重构 你的改法和我的方法3没有本质区别,但是性能结果不理想。 这个过程也许有一点点过。但是反重构我是不同意的。 软件本身就不是一个简单的事情。看到<<或者>>如果你觉得不舒服我只能说个人的理解不一样。 这个类一看就知道应该是底层协议用的,当然是比较底层的。 1)"0123456789ABCDEF".charAt(left) 2)char[] chs={'0','1'........'F'} charAt源码: public char charAt(int index) { if ((index < 0) || (index >= count)) { throw new StringIndexOutOfBoundsException(index); } return value[index + offset]; } 这两行代码的效率是不一样的。因为1)的代码比2)的代码多了一个方法调用、参数传递还有里边判断的过程。 这个的底层协议,导致数据长度比原来增加了一倍,这个方案本身就是粗糙的,然后拼命的提高代码效率... 个人喜好吧 |
|
返回顶楼 | |
发表时间:2010-12-18
gdpglc 写道 zhang_xzhi_xjtu 写道 gdpglc 写道 "0123456789ABCDEF".charAt(left)
地方还可以优化 char[] chs={'0','1'........'F'} chs[left] 不过我一定不会这么用。我宁可用Character.forDigit方法,这样的代码可读性太差了。这么在乎性能的理由是什么?你在写系统软件吗? 把乘2写成<<1,唉。我觉得真是精力过剩了.......... 这哪能叫重构.... 整个一个反重构 你的改法和我的方法3没有本质区别,但是性能结果不理想。 这个过程也许有一点点过。但是反重构我是不同意的。 软件本身就不是一个简单的事情。看到<<或者>>如果你觉得不舒服我只能说个人的理解不一样。 这个类一看就知道应该是底层协议用的,当然是比较底层的。 1)"0123456789ABCDEF".charAt(left) 2)char[] chs={'0','1'........'F'} charAt源码: public char charAt(int index) { if ((index < 0) || (index >= count)) { throw new StringIndexOutOfBoundsException(index); } return value[index + offset]; } 这两行代码的效率是不一样的。因为1)的代码比2)的代码多了一个方法调用、参数传递还有里边判断的过程。 这个的底层协议,导致数据长度比原来增加了一倍,这个方案本身就是粗糙的,然后拼命的提高代码效率... 个人喜好吧 我说了和3本质是一样的,麻烦能不能看看3. |
|
返回顶楼 | |
发表时间:2010-12-18
最后修改:2010-12-18
To gdpglc
不好意思,理解错了你说的代码的位置。 主贴已经更新。 这个只是性能提高的一个方面。 public char charAt(int index) { if ((index < 0) || (index >= count)) { throw new StringIndexOutOfBoundsException(index); } return value[index + offset]; } 还有一个方面是因为append的不同实现。 public AbstractStringBuilder append(String str) { if (str == null) str = "null"; int len = str.length(); if (len == 0) return this; int newCount = count + len; if (newCount > value.length) expandCapacity(newCount); str.getChars(0, len, value, count); count = newCount; return this; } public AbstractStringBuilder append(char c) { int newCount = count + 1; if (newCount > value.length) expandCapacity(newCount); value[count++] = c; return this; } |
|
返回顶楼 | |