字符串不可变性以及StringBuilder和StringBuffer在字符串拼接中的作用和扩容机制

本文主要是介绍字符串不可变性以及StringBuilder和StringBuffer在字符串拼接中的作用和扩容机制,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

java字符串的不可变性

在jdk1.8及以前,字符串底层存储用的是一个字符(char)类型的数组,jdk1.9之后用的是整型中的字节型(byte)数组来存储字符串。以下下主要以jdk1.8为例子展开。

 private final char value[];
/*
底层的字符类型的数组声明时用private和final来修饰
private修饰代表在类之外不能直接操作,只能通过该对象的公共方法来操作,而公共方法最后都返回的是新字符串
final来修饰代表引用数据类型的地址不能变,也就是说只能修改里面的元素内容,但是在外部无法直接操作,只能借助类中的方法,但是方法都返回一个新的字符串
从这两个角度,字符串无法变,这就是字符串的不可变性
*/对于一个方法的解析
public String substring(int beginIndex, int endIndex) {if (beginIndex < 0) {throw new StringIndexOutOfBoundsException(beginIndex);}if (endIndex > value.length) {throw new StringIndexOutOfBoundsException(endIndex);}int subLen = endIndex - beginIndex;if (subLen < 0) {throw new StringIndexOutOfBoundsException(subLen);}return ((beginIndex == 0) && (endIndex == value.length)) ? this: new String(value, beginIndex, subLen);}substring:字符串的截取, 当截取值就是整个字符串时,返回自己,没改变。如果不是new了一个新的String对象,原字符串没有发生改变。
通过这个方法可以了解到,/*无法在不改变内部代码的情况下,修改字符串的内容,那么字符串不可变*/

大量字符串拼接中StringBuilder和StringBuffer的作用

因为字符串的不可变性,字符串的拼接最终都是产生一个新的字符串,在大量拼接操作过程中就会产生大量的新字符串(临时字符串),将会短时间占用大量空间,导致空间的浪费。

StringBuilder、StringBuffer非相关拓展介绍:

StringBuilder:jdk1.5,非线程安全,稍慢

StringBuffer:jdk1.0,带线程锁(synchronized),线程安全,稍慢

下面以StringBuilder为例分析其在大量字符串拼接中的作用

StringBuilder类:
public final class StringBuilderextends AbstractStringBuilderimplements java.io.Serializable, CharSequenceAbstractStringBuilder抽象类:
abstract class AbstractStringBuilder implements Appendable, CharSequence    public StringBuilder(int capacity) {super(capacity);
}
AbstractStringBuilder(int capacity) {value = new char[capacity];}/*
StringBuilder这个类继承AbstractStringBuilder抽象类,StringBuilder这个类中构造方法调用了抽象类的构造方法,创建了一个字符(char)类型的数组。
*/至此我们可以创建一个类似于字符串的一个StringBuilder对象,并可以创建一个初始值,如果我在创建对象时使用的无参构造方法,底层会给我创建一个长度为16的字符类型的数组,作为成员会有默认值就是空字符
下面开始拼接操作,看字符串的拼接和StringBuilder对象来做拼接有什么不同
StringBuilder对象的拼接需要用到append方法
StringBuilder sc = new StringBuilder("123");
sc.append(",");public StringBuilder append(String str) {super.append(str);return this;}
public AbstractStringBuilder append(String str) {if (str == null)return appendNull();int len = str.length();ensureCapacityInternal(count + len);str.getChars(0, len, value, count);count += len;return this;}
private AbstractStringBuilder appendNull() {int c = count;ensureCapacityInternal(c + 4);final char[] value = this.value;value[c++] = 'n';value[c++] = 'u';value[c++] = 'l';value[c++] = 'l';count = c;return this;}
/*
sc这个对象在调用时用了父类的append方法,将自己返回
父类中的append方法,首先判断了一下传入变量是否为null,是执行appendNull()方法将结果返回
appendNull()方法:首先判断是否需要扩容,用另一个变量指向对象的字符数组,将null拆分成字符添加到字符数组中,直接改变对象底层的字符数组。
接着如果添加的元素不是null,就执行字符串的getChars实际是System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);将一个数组中的元素复制到另一个数组,前面的数组是源数组,后一个是目标数组,将元素组中的元素,复制到目标数组。此操作仍是在对象的底层字符数组上直接操作。
所以整个一次拼接过程不产生临时额外数组,完美的解决了原始字符串拼接,每次拼接操作都会产生临时数据浪费资源的问题
*/

StringBuffer与StringBuilder拼接过程基本一致,只是它们一个线程安全效率稍低,一个非线程安全效率比较而言稍高。

StringBuffer与StringBuilder中的扩容机制

仍然以StringBuilder为例

StringBuilder有多种构造方法生成对象,无参的,有参传数值的,有参传字符串的等
以无参、有参传数值、有参传字符串这三种常见的创建对象的形式,了解其中的扩容机制
StringBuilder sc = new StringBuilder();
public StringBuilder() {super(16);}
AbstractStringBuilder(int capacity) {value = new char[capacity];}
/*
无参,调用抽象类的构造方法,创建一个长度为16的字符数组,初始值空字符
*/
StringBuilder sc1 = new StringBuilder(1024);
public StringBuilder(int capacity) {super(capacity);}
AbstractStringBuilder(int capacity) {value = new char[capacity];}
/*
传数值的这种,自己定义最初字符数组长度
*/StringBuilder sc2 = new StringBuilder("123");
public StringBuilder(String str) {super(str.length() + 16);append(str);}
AbstractStringBuilder(int capacity) {value = new char[capacity];}
/*
传字符串这种,先调用抽象类的构造方法,得到一个字符串长度再加16的字符数组
只有调用append(str)方法,将字符串底层字符数组复制到对象底层维护的数组
*/
当以上三种方法创建出对象后做拼接操纵,会通过ensureCapacityInternal(count + len)方法来判断需不需要扩容
minimumCapacity=count + len,拼接后字符数组数据元素长度=原本的字符数组实际长度+要拼接字符串长度
当minimumCapacity - value.length > 0时,也就是拼接后字符数组元素长度>对象维护的数组长度时我们需要扩容value = Arrays.copyOf(value,newCapacity(minimumCapacity));通过newCapacity方法得到最终扩容后数组长度,通过Arrays.copyOf()复制对象维护数组值的同时开辟相应的空间,最终数组复制给对象维护字符数组。
newCapacity方法,首先在原对象维护数组长度基础上*2+2;此时看扩容后的长度是否满足最低要求,满足,扩容长度为原对象维护数组长度基础上*2+2,不满足则扩充最低要求长度,最终如果这个长度不小于等于0或者超过定义的最大长度,那么最终长度确定,否则和Integer.MAX_VALUE值作比较,比这个值都大,报异常内存溢出,否则和数组最大长度(MAX_ARRAY_SIZE)比较,如果小,就返回当前值,否则就把数组最大值返回。

这篇关于字符串不可变性以及StringBuilder和StringBuffer在字符串拼接中的作用和扩容机制的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/621341

相关文章

MySQL更新某个字段拼接固定字符串的实现

《MySQL更新某个字段拼接固定字符串的实现》在MySQL中,我们经常需要对数据库中的某个字段进行更新操作,本文就来介绍一下MySQL更新某个字段拼接固定字符串的实现,感兴趣的可以了解一下... 目录1. 查看字段当前值2. 更新字段拼接固定字符串3. 验证更新结果mysql更新某个字段拼接固定字符串 -

Java String字符串的常用使用方法

《JavaString字符串的常用使用方法》String是JDK提供的一个类,是引用类型,并不是基本的数据类型,String用于字符串操作,在之前学习c语言的时候,对于一些字符串,会初始化字符数组表... 目录一、什么是String二、如何定义一个String1. 用双引号定义2. 通过构造函数定义三、St

golang获取当前时间、时间戳和时间字符串及它们之间的相互转换方法

《golang获取当前时间、时间戳和时间字符串及它们之间的相互转换方法》:本文主要介绍golang获取当前时间、时间戳和时间字符串及它们之间的相互转换,本文通过实例代码给大家介绍的非常详细,感兴趣... 目录1、获取当前时间2、获取当前时间戳3、获取当前时间的字符串格式4、它们之间的相互转化上篇文章给大家介

Java编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、

C#数据结构之字符串(string)详解

《C#数据结构之字符串(string)详解》:本文主要介绍C#数据结构之字符串(string),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录转义字符序列字符串的创建字符串的声明null字符串与空字符串重复单字符字符串的构造字符串的属性和常用方法属性常用方法总结摘

Java实现时间与字符串互相转换详解

《Java实现时间与字符串互相转换详解》这篇文章主要为大家详细介绍了Java中实现时间与字符串互相转换的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、日期格式化为字符串(一)使用预定义格式(二)自定义格式二、字符串解析为日期(一)解析ISO格式字符串(二)解析自定义

java中反射(Reflection)机制举例详解

《java中反射(Reflection)机制举例详解》Java中的反射机制是指Java程序在运行期间可以获取到一个对象的全部信息,:本文主要介绍java中反射(Reflection)机制的相关资料... 目录一、什么是反射?二、反射的用途三、获取Class对象四、Class类型的对象使用场景1五、Class

C++ 中的 if-constexpr语法和作用

《C++中的if-constexpr语法和作用》if-constexpr语法是C++17引入的新语法特性,也被称为常量if表达式或静态if(staticif),:本文主要介绍C++中的if-c... 目录1 if-constexpr 语法1.1 基本语法1.2 扩展说明1.2.1 条件表达式1.2.2 fa

css中的 vertical-align与line-height作用详解

《css中的vertical-align与line-height作用详解》:本文主要介绍了CSS中的`vertical-align`和`line-height`属性,包括它们的作用、适用元素、属性值、常见使用场景、常见问题及解决方案,详细内容请阅读本文,希望能对你有所帮助... 目录vertical-ali

python中字符串拼接的几种方法及优缺点对比详解

《python中字符串拼接的几种方法及优缺点对比详解》在Python中,字符串拼接是常见的操作,Python提供了多种方法来拼接字符串,每种方法有其优缺点和适用场景,以下是几种常见的字符串拼接方法,需... 目录1. 使用 + 运算符示例:优缺点:2. 使用&nbsjsp;join() 方法示例:优缺点:3