读源码学编程之——死循环妙用

现假设有一个整型数组:

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.

原文地址:https://www.cnblogs.com/weix-l/p/12635362.html

时间: 2024-10-11 05:11:06

读源码学编程之——死循环妙用的相关文章

Java读源码学设计模式:适配器Adapter

适配器模式相关源码:slf4j-1.6.1.hibernate-3.6.7 大家都知道,log4j是一个广泛使用的日志工具,除此之外,sun公司在JDK中也有自己的日志工具,也就是java.util.logging.Logger.当然还有其他一些日志工具. 多种日志工具功能和使用方式类似,一般都包含debug.info.warn.error等日志级别的方法,但却没有实现共同的接口.这一点不像JDBC,虽然关系型数据库种类很多,例如MySQL.Oracle等,但是有一套统一的接口,也就是JDBC.

从LLVM源码学C++(四)

关键知识点:断言 1 const Option OptTable::getOption(OptSpecifier Opt) const { 2 unsigned id = Opt.getID(); 3 if (id == 0) 4 return Option(0, 0); 5 assert((unsigned) (id - 1) < getNumOptions() && "Invalid ID."); 6 return Option(&getInfo(id

这样读源码,不牛X也难

程序员在工作过程中,会遇到很多需要阅读源码的场景,比如技术预研.选择技术框架.接手以前的项目.review他人的代码.维护老产品等等.可以说,阅读源代码是程序员的基本功,这项基本功是否扎实,会在很大程度上影响一个程序员在技术上的成长速度. 2014年写<Qt on Android核心编程>和<Qt Quick核心编程>时,很多内容都是通过分析Qt源码搞明白的.这阵子研究CEF和PPAPI,也主要靠研究源代码来搞明白用法.最近工作上要修改已有项目的一个子系统,也是得硬着头皮先读懂代码

杂谈篇之我是怎么读源码的,授之以渔

前言 开心一刻 今天上课不小心睡着了,结果被老师叫起来回答问题,这是背景.无奈之下看向同桌寻求帮助,同桌小声说到选C,结果周围的人都说选C,向同桌投去一个感激的眼神后大声说道选C.刚说完教室就笑开了,老师一脸恨铁不成钢的表情说选你个头,我叫你翻译文言文你选C!你出去,你给我出去.看着同桌挤眉弄眼的表情,劳资真想说,这帮畜生 路漫漫其修远兮,吾将上下而求索! github:https://github.com/youzhibing 码云(gitee):https://gitee.com/youzh

杂谈篇之我是怎么读源码的

读源码的经历 刚参加工作那会,没想过去读源码,更没想过去改框架的源码:总想着别人的框架应该是完美的.万能的,应该不需要改:另外即使我改了源码,怎么样让我的改动生效了? 项目中引用的不还是没改的jar包吗.回想起来觉得那时候的想法确实挺…… 工作了一年多之后准备跳槽了,开始了一轮的面试,其中有几个面试官就问到了相关的源码问题:ArrayList.HashMap的底层实现,spring.mybatis的相关源码.问源码的面试一般就是回去等消息,然后就没然后了. 那时候开始意识到,源码这东西在之前的工

[一起读源码]走进C#并发队列ConcurrentQueue的内部世界

决定从这篇文章开始,开一个读源码系列,不限制平台语言或工具,任何自己感兴趣的都会写.前几天碰到一个小问题又读了一遍ConcurrentQueue的源码,那就拿C#中比较常用的并发队列ConcurrentQueue作为开篇来聊一聊它的实现原理. 话不多说,直奔主题. 要提前说明下的是,本文解析的源码是基于.NET Framework 4.8版本,地址是:https://referencesource.microsoft.com/#mscorlib/system/Collections/Concur

从LLVM源码学C++(五)

知识点:static,const,static const 详解:转(http://blog.csdn.net/yjkwf/article/details/6067267) const定义的常量在超出其作用域之后其空间会被释放,而static定义的静态常量在函数执行后不会释放其存储空间. static表示的是静态的.类的静态成员函数.静态成员变量是和类相关的,而不是和类的具体对象相关的.即使没有具体对象,也能调用类的静态成员函数和成员变量.一般类的静态函数几乎就是一个全局函数,只不过它的作用域限

从LLVM源码学C++(三)

关键知识点:mutable关键字 mutable关键字: mutalbe的中文意思是“可变的,易变的”,跟constant(既C++中的const)是反义词. 在C++中,mutable也是为了突破const的限制而设置的.被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中. 我们知道,如果类的成员函数不会改变对象的状态,那么这个成员函数一般会声明成const的.但是,有些时候,我们需要在const的函数里面修改一些跟类状态无关的数据成员,那么这个数据成员就应该被mut

从LLVM源码学C++(一)

今天开始需要分析clang的源码了,LLVM这个开源的project代码写的很不错的,也算是巩固一下C++的一些基础知识了. 首先是在llvm/ADT/OwningPtr.h中定义了owningptr智能指针的实现: 源码如下: 1 /// OwningPtr smart pointer - OwningPtr mimics a built-in pointer except that it 2 /// guarantees deletion of the object pointed to,