java 中 String 拼接的问题

java 中 String 拼接的问题


今天看到网上一个关于代码效率优化的视频,对下面这段字符串拼接的代码进行优化。因为上面的方式会多在栈中多创建一个引用。

修改前:

for (int i = 0; i < 1000_0000; i++) {
    String v1 = map.get("k1");
    String v2 = map.get("k2");
    String s = v1 + " " + v2;
}

修改后:

for (int i = 0; i < 1000_0000; i++) {
    String s = map.get("k1") + "-" + map.get("k2");
}

如果是为了节省空间这样优化没问题 (但是感觉也收效甚微),我想起以前也遇到过类似这种问题,从速度上讲,执行速度上是上面这种方式更快。

于是我就又写了个demo测试一下( jdk1.8 )。如下:

  • 1.使用第一种拼接方式
    public static void main(String[] args) {
        long be = System.currentTimeMillis();
        Map<String, String> map = new HashMap<>(2, 1);
        map.put("k1", "v1");
        map.put("k2", "v2");

        for (int i = 0; i < 1000_0000; i++) {
            String v1 = map.get("k1");
            String v2 = map.get("k2");
            String s = v1 + "-" + v2;

        }
        System.out.println(System.currentTimeMillis() - be);
    }

在我的电脑上,结果大概是 186 毫秒左右

  • 2。使用第二种拼接方式
    public static void main(String[] args) {
        long be = System.currentTimeMillis();
        Map<String, String> map = new HashMap<>(2, 1);
        map.put("k1", "v1");
        map.put("k2", "v2");

        for (int i = 0; i < 1000_0000; i++) {
            String s = map.get("k1") + "-" + map.get("k2");
        }
        System.out.println(System.currentTimeMillis() - be);
    }

在我的电脑上,结果大概是 453 毫秒左右

结果是第一种方式比第二种快了很多。为什么?

因为常量池优化。

这也是面试八股文的核心,每个 java 仔可能都背得滚瓜烂熟,但是实际使用可能一不小心就忽略了。这其实算是一个经验或者说玄学,因为鲜有人知道 jvm 里的具体实现,所以也都是硬背概念。

简言之就是:直接用字符串或者字符串变量拼接创建String对象,会去检查字符串常量池,池中没有就在池中创建一个,有则直接使用,不会在堆中再重复创建。如下:s的创建就有常量池优化

String a = "a";
String b = "b";
String s = a + b + "c";

由于我测试的demo写死的 map 的 key (k1 和 k2),所以 s = v1 + “-” + v2 也是固定值,在第一次创建后就一直在常量池里。所以方式1会比方式2快非常多。

理论如此,那如何证明确实是常量池优化呢?

我改一下demo,用不重复的 map 值来拼接,如下: 每次 get 到的值都是不同的,所以 s 每次都不相同

  • 1.采用方式1
public static void main(String[] args) {
        long be = System.currentTimeMillis();
        Map<String, String> map = new HashMap<>(2, 1);
        for (int i = 0; i < 1000_0000; i++) {
            map.put(Integer.toString(i),Integer.toString(i));
        }

        for (int i = 0; i < 1000_0000; i++) {
            String v1 = map.get(Integer.toString(i));
            String v2 = map.get(Integer.toString(i));
            String s = v1 + "-" + v2;
        }
        System.out.println(System.currentTimeMillis() - be);
    }

在我的电脑上,结果大概是 13427 毫秒左右

  • 采用方式2
public static void main(String[] args) {
        long be = System.currentTimeMillis();
        Map<String, String> map = new HashMap<>(2, 1);
        for (int i = 0; i < 1000_0000; i++) {
            map.put(Integer.toString(i), Integer.toString(i));
        }

        for (int i = 0; i < 1000_0000; i++) {
            String s = map.get(Integer.toString(i)) + "-" + map.get(Integer.toString(i));
        }
        System.out.println(System.currentTimeMillis() - be);
    }

在我的电脑上,结果大概是 13778 毫秒左右

很明显,这一次每次拼接的字符串都不相同,所以常量池优化没起作用,最后运行结果相差无几。

上面测试了拼接字符串 全部相同全部不同 的情况,

最后再修改下demo为,部分相同,如下。(提前预测,第一种方式还是会快很多)

  • 方式1
public static void main(String[] args) {
        long be = System.currentTimeMillis();
        Map<String, String> map = new HashMap<>(2, 1);
        for (int i = 0; i < 1000; i++) {
            map.put(Integer.toString(i), Integer.toString(i));
        }

        for (int i = 0; i < 1000_0000; i++) {
            String v1 = map.get(Integer.toString(i % 1000));
            String v2 = map.get(Integer.toString(i % 1000));
            String s = v1 + "-" + v2;
        }
        System.out.println(System.currentTimeMillis() - be);
    }

在我的电脑上,结果大概是 747 毫秒左右

  • 方式2
public static void main(String[] args) {
        long be = System.currentTimeMillis();
        Map<String, String> map = new HashMap<>(2, 1);
        for (int i = 0; i < 1000; i++) {
            map.put(Integer.toString(i), Integer.toString(i));
        }

        for (int i = 0; i < 1000_0000; i++) {
            String s = map.get(Integer.toString(i % 1000)) + "-" + map.get(Integer.toString(i % 1000));
        }
        System.out.println(System.currentTimeMillis() - be);
    }

在我的电脑上,结果大概是 1003 毫秒左右

预测正确。

所以结论:如果拼接的字符串有很多重复的,那么一定要采用方式1,有常量池的优化速度会快很多,在栈里多创建一个引用的影响也微乎其微。

如果拼接的字符串没有或者极少重复,那使用方式1或2都可以。方式2会更省空间一点点,看自己的取舍。

你可能感兴趣的:(java,java基础,java)