读源码学编程之——死循环妙用
现假设有一个整型数组:
Integer[] arr = {20, 20, 4, 4, 21, 7}; // 2020年4月4日21时7分
如何用逗号加空格 “, ” 分割数组元素并放置在“[]”中从而获得如下格式的数组元素字符串呢?
[20, 20, 4, 4, 21, 7]
Show you the code. 我是这样写的:
public static String toString(Object[] arr) {
if (arr == null) {
return "null";
}
StringBuilder result = new StringBuilder("[");
for (int i = 0; i < arr.length; i++) {
result.append(arr[i]);
if (i < arr.length - 1) {
result.append(", ");
}
}
return result.append("]").toString();
}
在当前类进行单元测试:
1. 数组长度为0时应该返回 []
@Test
public void printArray1() {
Object[] arr = {};
Assert.assertEquals("[]", toString(arr));
}
2. 数组元素为上述数组元素时应该返回 [20, 20, 4, 4, 21, 7]
@Test
public void printArray2() {
Object[] arr = {20, 20, 4, 4, 21, 7};
Assert.assertEquals("[20, 20, 4, 4, 21, 7]", toString(arr));
}
经测试方法是没有问题的。
JDK 的 java.util.Arrays 工具类中已经有一个方法可以实现该功能,源码粘贴如下:
public static String toString(Object[] a) {
if (a == null)
return "null";
int iMax = a.length - 1;
if (iMax == -1)
return "[]";
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0; ; i++) {
b.append(String.valueOf(a[i]));
if (i == iMax)
return b.append(']').toString();
b.append(", ");
}
}
都是遍历数组借助 StringBuilder 拼接元素的方式实现,不同之处分析如下。
1. 数组长度为0时的处理方式
JDK中判断了数组长度为0的情况,此时直接返回一个“[]”;我并没有特别处理这种情况,而是通过当数组长度为0时 for 循环不会执行间接控制,这种做法不好的地方是当数组长度为0时也会创建一个 StringBuilder 对象。
2. Object 数组元素是否要转换为 String 再拼接
我直接将Object数组元素通过 StringBuilder 的 append(Object) 方法进行拼接,但是JDK 的处理方式是将 Object 数组的每一个元素都调用 String.valueOf(Object) 方法转换成 String 后再拼接,这样做的好处可能是易读性更好,但从代码整洁的角度看这样做多此一举,因为 StringBuilder 的 append(Object) 方法内部本身会将 Object 参数转换为 String:
@Override
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
3. 循环的控制和最后一个元素后无需拼接分隔符的处理方式
我用常规的 i < arr.length 的方式来来控制数组的遍历,并显式地特殊处理了最后一个元素(if (i < arr.length - 1))不拼接分隔符的情况;JDK 的做法则更高明,在 for 循环中用的是死循环,在遍历到最后一个元素时拼接结束的右括号后直接返回,这样做可以避免像我那样在 for 循环语句和循环体中分别判断最后在一个元素时应该执行的动作,执行效率更高。
结论
总体来看我的实现方式很常规,JDK 的实现显然更高明。
之前一直对死循环这一单纯听名字就可能产生灾难性问题的做法敬而远之,以为非特殊情况根本不会用到,通过此次比较发现,如果循环时需要处理特殊情况并在这种特殊情况时需要终止循环,则可以直接通过这种特殊情况控制循环,而无需单独作控制,这样可以显著提高程序执行效率。
综上,将 java.util.Arrays的方法作简单优化,去掉显示转换 Object 为 String 的方法并替换为 StringBuilder 的 append(Object)方法,得到如下方法:
public static String toString(Object[] a) {
if (a == null)
return "null";
int iMax = a.length - 1;
if (iMax == -1)
return "[]";
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0; ; i++) {
b.append(a[i]);
if (i == iMax)
return b.append(']').toString();
b.append(", ");
}
}
我们的口号是:Learn from the best and be the best.