新版Java为什么要修改substring的实现
“参考这位大神的”
1.8版本的substring
// JDK1.7之后的substring public String substring(int beginIndex) { if(beginIndex<0) { throw new StringIndexOutOfBoundsException(beginIndex); } int subLen = this.value.length - beginIndex; if(subLen < 0) { throw new StringIndexOutOfBoundsException(subLen); } return (beginIndex == 0)?this:new String(**value**,beginIndex,subLen); } public String(char value[], int offset, int count) { if(offset < 0) { throw new StringIndexOutOfBoundsException(offset); } if(count <= 0) { if(count < 0) { throw new StringIndexOutOfBoundsExcetpion(count); } if(offset <= value.length) { this.**value** = "".**value**; return; } } if(offset > value.length - count) { throw new StringIndexOutOfBoundsException(offset + count); } this.**value** = Arrays.copyOfRange(value, offset, offset + count); }value:String私有的char[ ] value
满足所有的条件时,substring调用了一个构造器,在这个构造器里直接拷贝数组,赋值给自己的私有变量 (外部无法访问),从而创建一个新的字符串(String创建字符串,是使用char[ ]) 。 “修改后的substring的效率变低了,并且占用了更多的内存,无论是从时间上还是空间上都比不上原有的实现”
//JDK1.7之前的substring public String substring(int beginIndex) { //省略判断,按理想条件走 return ((beginIndex == 0) && (endIndex == count)) ? this : new String(offset + beginIndex, endIndex - beginIndex,value); } // Package PRivate constructor which shares value array for speed. String(int offset, int count, char value[]) { this.value = value; this.offset = offset; this.count = count; } //通过复用数组value,省去了数组拷贝的开销,仅通过3个赋值语句就创建了一个新的字符串对象改变前: 性能好,效率高 改变后: “掩盖了优点”
那为什么要修改substring呢?为什么要牺牲其性能呢?
因为安全吗?复用数组有安全隐患吗?
旧版JDK:创建一个String对象,通过复制参数内部的数组(this.value=value)实现 。但不会有安全问题:比方说一个字符数组arr,原来是{‘h’,’e’,’l’,’l’,’o’},作为参数传给了String的构造方法,创建了一个String对象;当改变arr的内容时,创建的那个String对象没有变化(因为复制是使用的String私有的char[ ],外部无法访问)。
arr=new arr[]{'h','e','l','l','o'}; str = new String(0, arr.length, arr) // 顺序和新版的不一样 System.out.print(str); // hello arr[0]='A'; System.out.print(str); // hello 木有改变新版JDK:创建一个String对象,通过Arrays.copyOf方法实现 。更不会有安全问题了,来一个参数就创建一个新的String对象,即使两个参数一模一样,创建出来的对象也不相等(==)。
木有安全问题,那为什么要改变呢? 我查了一下2011年互联网发生了啥事,好多 。
因为旧版的可能存在内存泄漏的问题 。 从网络上抓个东西(A),假设100个字节长度,将A变为String对象(B),只截取其中的10个字节长度(C) 。问题来了,假设GC把A清理走了,但B还保留着A的100个字节长度的东西,导致内存泄漏 (和内存溢出的区别,参看别人的总结不让转载的,链接过去不知有没有问题啊?) 。 新版的可以把字符串对象和内部数组一起回收,更加健壮!!
总结
旧版的substring性能好,但在某些情况下内存泄漏是个很严重的问题;新版的substring更加健壮,却牺牲了性能 。孰优孰劣,无从评判~~