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 
    7 dup
    8 invokespecial #4 >
    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 
    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     //创建StringBuilder
    21 dup
    22 invokespecial #5 >    //初始化StringBuilder
    25 aload_3
    26 invokevirtual #6     //扩展str现在的值
    29 iload 4
    31 invokevirtual #7     //扩展现在的i
    34 invokevirtual #8   //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,大致就是正则找到需要替换的部分,然后循环去替换,而且又需要一堆异常判断就比较耗时。

你可能感兴趣的:(Java基础-String)