作者 | 浩说编程 来源 | 公众号:浩说编程 [ 大厂技术资源 | 研发必备安装包 | 限时免费获取 ] String在日常开发中的使用频率应该不需要我过多形容,大家闭着眼睛都能手写出来,但也正因如此,对于String的性能优化最容易被忽视却也最为必要! 如何优化String的内存开支? 怎样用更少的空间管理String? 让我们带着这几个问题开始本篇的探讨。一、String自身的"优化"设计思想"String 和 StringBuffer 有什么区别?" "StringBuffer可变,String不可变。" "String为什么不可变?" "额…" 这是一段面试的真实案例,面试者直接被pass掉,这也反映了一个普遍的问题:"知其然而不知其所以然",希望大家引以为鉴,下面就来探讨一下"String为什么不可变?" 先从一个例子开始:String str_1 = "abc"; String str_2 = "abc"; System.out.print(str_1==str_2);//true 123 相信大家都知道答案,重点来拆解一下每一步的底层逻辑: 1、String str_1 = "abc"; 首先,在常量池中查询"abc"是否已存在: 发现不存在,于是将"abc"初始化到常量池中,str_1引用该内容: 2、String str_2 = "abc"; 再次创建相同字符串"abc",同样在常量池中查询"abc"是否已存在 判断已经存在,于是不再重复创建内容,使用相同的引用: 于是得出答案true。 那么试想一下,如果String可变的话,这种常量池存放字符串,内容相同则不再开辟空间而是指向同一引用的设计思想是否还可行? 是不是即便创建了相同的字符串也要重新分配内存,造成不必要的内存损耗。这就是String自身的"优化"设计思想。 码文不易 你的关注是浩说编程持续更新的动力 浩说编程会做的更好 二、Stirng.intern:从20G到百兆内存消耗 该优化方法分享自全球知名社交平台推特(Twitter)的一名工程师,使用这种方法成功帮助他将String的内存消耗从20G降到几百兆。 我们通过代码来看一下intern方法做了什么:String a = new String("abc").intern(); String b = new String("abc").intern(); if(a==b) { System.out.print("a==b"); } 12345 创建 a 变量时,调用 new Sting() 会在堆内存中创建一个 String 对象,String 对象中的 char 数组将会引用常量池中字符串。在调用 intern 方法之后,会去常量池中查找是否有等于该字符串对象的引用,有就返回引用。 创建 b 变量时,调用 new Sting() 会在堆内存中创建一个 String 对象,String 对象中的 char 数组将会引用常量池中字符串。在调用 intern 方法之后,会去常量池中查找是否有等于该字符串对象的引用,有就返回引用。 而在堆内存中的两个对象,由于没有引用指向它,将会被垃圾回收。所以 a 和 b 引用的是同一个对象。 三、纠正字符串拼接误区 按照正常的理解,由于String不可变,那么每次运算操作都应该产生了一个新的对象,事实真的如此吗?String str = "a" + "b" + "c"; 1 我将上面代码编译成的class文件通过反编译之后的结果:String str = "abc"; 1 所以事实是:在拼接String常量的时候,编译器对代码进行了优化,将字符串合并了。 我们再试一下拼接变量的效果:String b = "b"; String str = "a" + b + "c"; 12 编译之后:String b = "b"; String str = (new StringBuilder()).append("a").append(b).append("c"); 12 虽然编译器对String进行了优化,不过还是建议显示的使用StringBuffer或StringBuilder。 四、字符串分割的最优解 对于字符串的分割,我在上一篇讲到过split()方法中正则表达式的回溯引起的效率问题,所以建议采用indexOf()方法作字符串分割。 作者 | 浩说编程 来源 | 公众号:浩说编程