Java基础-String

周末在做一道题的时候用了String.format来生成hash值,结果一直运行时间过长,于是就梳理下String相关的知识。

String.format("%09d%0d",i,j);//非常耗时

字符串是否相等

首先是看一个判断String是否相等的问题

    String a = "hello";

    String b = "hello";

    System.out.println(a==b);
    // true

在第一次使用hello字符串时,会创建字符串并存入常量池中,重复使用时,就从常量池取出,于是再多的相同字符串的变量都是相等的。

在创建了变量c,使用new String的方式,比较a和c

    String c = new String("hello");
    System.out.println(a==c);
    //false

我们首先是创建了String的引用,然后再将String的引用指向常量池中的“hello”,所以结果是false。在IDE写这条new语句时实际就提示了 'new String' is redundant 这种写法是多余的。

String的类中提供了一个intern方法,注释上说:

 * When the intern method is invoked, if the pool already contains a
 * string equal to this {@code String} object as determined by
 * the {@link #equals(Object)} method, then the string from the pool is
 * returned. Otherwise, this {@code String} object is added to the
 * pool and a reference to this {@code String} object is returned.

如果常量池中有这个String相等时就返回,否则将这个String的对象假如到常量池中并返回它的引用。这是一个忽略包装的对象,直接从常量池中取到字符串的方法。

    System.out.println(a== c.intern());
    //true

运行下代码与预期相符。

字符串拼接问题

实际开发过程中我们经常会拼接一些字符串用于展示。如 单价:25元。在一些开发规范中经常告诉我们不要直接“+”的形式,用StringBuilder效率更高。我们就用代码举个例子试一下。

    long startTime = System.currentTimeMillis();
    String str = "";
    for (int i = 0; i < 10000; i++) {
        str += i;
    }

    System.out.println(System.currentTimeMillis() - startTime);

使用“+”的方式将0~9999共一万个数字进行拼接,时间大约500ms。
再换成StringBuilder的方式

    StringBuilder builder = new StringBuilder();
    for (int i = 0; i < 10000; i++) {
        builder.append(i);
    }

只需10ms,效率相差50倍。
我们通过javap查看字节码来研究这个问题。

    4 new #3 <java/lang/StringBuilder>
    7 dup
    8 invokespecial #4 <java/lang/StringBuilder.<init>>
    11 astore_3
    12 iconst_0
    13 istore 4
    15 iload 4
    17 sipush 10000
    20 if_icmpge 36 (+16)
    23 aload_3
    24 iload 4
    26 invokevirtual #5 <java/lang/StringBuilder.append>
    29 pop
    30 iinc 4 by 1
    33 goto 15 (-18)

这是StringBuilder方式生成的字节码,可以看到StringBuilder的创建,调用append方法,和循环(go 15)

接下来看“+”拼接的方式

     4 ldc #3   //给String赋值
     6 astore_3
     7 iconst_0
     8 istore 4
     10 iload 4
     12 sipush 10000
     15 if_icmpge 44 (+29)
     18 new #4 <java/lang/StringBuilder>    //创建StringBuilder
    21 dup
    22 invokespecial #5 <java/lang/StringBuilder.<init>>    //初始化StringBuilder
    25 aload_3
    26 invokevirtual #6 <java/lang/StringBuilder.append>    //扩展str现在的值
    29 iload 4
    31 invokevirtual #7 <java/lang/StringBuilder.append>    //扩展现在的i
    34 invokevirtual #8 <java/lang/StringBuilder.toString>  //toString转回String
    37 astore_3
    38 iinc 4 by 1
    41 goto 10 (-31)

通过注释可以看出,使用“+”号拼接字符串真是令人窒息的操作,每一次循环都需要创建一个StringBuilder,添加现有值,再append循环的i,最后还要转回String赋值给str。

为什么使用StringBuilder的原因找到了,接下来回到文章开头,看String.format究竟做了什么,本想也看下字节码,看也看不懂。一步步下去找到 \java\util\Formatter.java,大致就是正则找到需要替换的部分,然后循环去替换,而且又需要一堆异常判断就比较耗时。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • String String是JAVA中最常用的对象,就是这样一个最常用最普通的对象,当你深入研究时却发现我们并不是...
    luoyoub阅读 2,990评论 1 0
  • 首先,来一道String的考题,代码如下: 先不着急给出答案,我们来一步一步得进行分析,争取覆盖String大部分...
    HRocky阅读 2,187评论 0 0
  • 常量池 当我们new一个String对象的时候,如果常量池中已经存在,则直接引用,也就是此时只会创建一个对象,如果...
    16325阅读 1,222评论 0 0
  • 其他更多java基础文章:java基础学习(目录) 学习资料:String类API中文深入解析String#int...
    Hiwayz阅读 4,581评论 0 1
  • 其他更多java基础文章:java基础学习(目录) 距离上次写文章已经好一段时间了,主要是工作忙起来,看书的时间就...
    Hiwayz阅读 4,014评论 0 0