美文网首页
Java基础-String

Java基础-String

作者: C调路过 | 来源:发表于2020-03-24 01:31 被阅读0次

    周末在做一道题的时候用了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,大致就是正则找到需要替换的部分,然后循环去替换,而且又需要一堆异常判断就比较耗时。

    相关文章

      网友评论

          本文标题:Java基础-String

          本文链接:https://www.haomeiwen.com/subject/lglnyhtx.html