`
zhang_xzhi_xjtu
  • 浏览: 538487 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

实践中的重构11_茴香豆的多种写法

阅读更多
鲁迅先生的名文孔乙己中,孔乙己纠结于茴香豆的茴字有多种写法,可惜小孩子不愿意学,可惜了孔乙己的一片良苦用心。
一个功能,不同的程序员的实现,基于各种因素,可能是千差万别的。即使是同一个程序员,一般也会有多种方式来完成该程序的编写。面对各种不同的可能实现方式,挑选出适合当前场景的实现,就变成了程序员责无旁贷的任务。

	/**
	 * 这个是最早的程序版本,把一个字符串转换成一个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
分享到:
评论
7 楼 gdpglc 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)的代码多了一个方法调用、参数传递还有里边判断的过程。

这个的底层协议,导致数据长度比原来增加了一倍,这个方案本身就是粗糙的,然后拼命的提高代码效率...

个人喜好吧
6 楼 pop1030123 2010-12-17  
这个类还有什么用处?还可以用来在字符编码间转码吧?
5 楼 zhang_xzhi_xjtu 2010-12-17  
gdpglc 写道
"0123456789ABCDEF".charAt(left)
地方还可以优化
char[] chs={'0','1'........'F'}
chs[left]

不过我一定不会这么用。我宁可用Character.forDigit方法,这样的代码可读性太差了。这么在乎性能的理由是什么?你在写系统软件吗?

把乘2写成<<1,唉。我觉得真是精力过剩了..........

这哪能叫重构.... 整个一个反重构


你的改法和我的方法3没有本质区别,但是性能结果不理想。

这个过程也许有一点点过。但是反重构我是不同意的。
软件本身就不是一个简单的事情。看到<<或者>>如果你觉得不舒服我只能说个人的理解不一样。
这个类一看就知道应该是底层协议用的,当然是比较底层的。
4 楼 whaosoft 2010-12-17  
这好用吗??
3 楼 gdpglc 2010-12-17  
"0123456789ABCDEF".charAt(left)
地方还可以优化
char[] chs={'0','1'........'F'}
chs[left]

不过我一定不会这么用。我宁可用Character.forDigit方法,这样的代码可读性太差了。这么在乎性能的理由是什么?你在写系统软件吗?

把乘2写成<<1,唉。我觉得真是精力过剩了..........

这哪能叫重构.... 整个一个反重构
2 楼 wumingshi 2010-12-17  
效率肯定高了但是可读性基本上没有任何好转。
1 楼 yangyi 2010-12-16  
很不错,看不出什么继续优化的地方了

相关推荐

Global site tag (gtag.js) - Google Analytics