字符串不可变性以及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)比较,如果小,就返回当前值,否则就把数组最大值返回。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/632359.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

新能源汽车出海业务之报关

引言 在做中国新能源汽车出海业务的信息化建设过程&#xff0c;秉承着深入了解业务的原则&#xff0c;对业务全链路进行学习了解总结&#xff0c;本文是针对出口报关业务环节的一些个人积累总结&#xff0c;供与诸位交流学习。 业务概述 报关是指在国际贸易中&#xff0c;出口…

Day34 贪心算法 part03 1005. K 次取反后最大化的数组和 134. 加油站 135. 分发糖果

贪心算法 part03 1005. K 次取反后最大化的数组和 134. 加油站 135. 分发糖果 1005. K 次取反后最大化的数组和 思路 第一步&#xff0c;从前向后遍历&#xff0c;遇到负数将其变为正数&#xff0c;同时K–第二步&#xff1a;如果K还大于0&#xff0c;那么反复转变数值最小的…

Marin说PCB之关于1000 BASE-T1--ESD的处理知多少?

对于板子上的ESD器件想必大家做硬件或者是layout应该的不陌生吧&#xff0c;我们几乎遇到大部分板子上面的接口部分都会添加这个ESD器件&#xff0c;例如那些USB,MIPI接口&#xff0c;百兆/千兆-T1以太网连接器等。 其中T1连接器用的是罗森博格家的&#xff0c;在这个链路中有一…

腾讯云轻量化应用服务器_轻量化应用服务器_轻量化私有云

腾讯云轻量应用服务器开箱即用、运维简单的轻量级云服务器&#xff0c;CPU内存带宽配置高并且价格特别便宜&#xff0c;大带宽&#xff0c;但是限制月流量&#xff0c;轻量2核2G3M带宽62元一年、2核2G4M优惠价118元一年&#xff0c;540元三年、2核4G5M带宽218元一年&#xff0c…

第二百七十四回

文章目录 1. 概念介绍2. 方法与类型2.1 使用方法2.2 常见类型 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何加载本地图片"相关的内容&#xff0c;本章回中将介绍如何获取文件类型.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在本章回…

[python]变量与常量

变量 语法结构&#xff1a; 变量名valueluck_number8在堆内存中开一块空间&#xff0c;放入8&#xff0c;栈内存中变量名luck_number指向堆当中的内存空间 通过赋不同类型的值&#xff0c;可以直接动态修改python变量的数据类型 在python中允许多个变量指向同一个值 nonum…

Docker 仓库管理

Docker 仓库管理 仓库&#xff08;Repository&#xff09;是集中存放镜像的地方。以下介绍一下 Docker Hub。当然不止 docker hub&#xff0c;只是远程的服务商不一样&#xff0c;操作都是一样的。 Docker Hub 目前 Docker 官方维护了一个公共仓库 Docker Hub。 大部分需求…

如何在MinIO存储服务中通过Buckets实现远程访问管理界面上传文件

文章目录 前言1. 创建Buckets和Access Keys2. Linux 安装Cpolar3. 创建连接MinIO服务公网地址4. 远程调用MinIO服务小结5. 固定连接TCP公网地址6. 固定地址连接测试 前言 MinIO是一款高性能、分布式的对象存储系统&#xff0c;它可以100%的运行在标准硬件上&#xff0c;即X86等…

chatgpt和文心一言哪个更好用

ChatGPT和文心一言都是近年来备受关注的人工智能语言模型。它们在智能回复、语言准确性、知识库丰富度等方面都有着较高的表现。然而&#xff0c;它们各自也有自己的特点和优势。在本文中&#xff0c;我们将从这几个方面对这两个模型进行比较&#xff0c;以帮助您更好地了解它们…

C#: richTextBox 富文本编辑控件使用

说明&#xff1a;在C#中&#xff0c;RichTextBox 是一个非常有用的控件&#xff0c;它允许用户在 Windows Forms 应用程序中编辑富文本格式的文本。RichTextBox 控件提供了许多功能&#xff0c;如字体、颜色、背景颜色、下划线、删除线、项目符号和编号列表等。 1.创建一个简单…

ArcGIS Pro 标注牵引线问题

ArcGIS Pro 标注 模仿CAD坐标牵引线问题 右键需要标注的要素&#xff0c;进入标注属性。 选择背景样式 在这里有可以选择的牵引线样式 选择这一个&#xff0c;可以根据调整间距来进行模仿CAD标注样式。 此图为cad样式 此为调整后gis样式 此处可以调整牵引线的样式符号 …

ClickHouse学习笔记(六):ClickHouse物化视图使用

文章目录 1、ClickHouse 物化视图2、物化视图 vs 普通视图3、物化视图的优缺点4、物化视图的用法4.1、基本语法4.2、准备表结构4.3、准备数据4.4、查询结果 1、ClickHouse 物化视图 ClickHouse 的物化视图是一种查询结果的持久化&#xff0c;它的存在是为了带来查询效率的提升…

KubeSphere 开源社区 2023 年度回顾与致谢

2023 年结束了&#xff0c;让我们再一次一起回顾一下 KubeSphere 开源社区在过去一年的变化。更重要的是&#xff0c;本篇文章将会对 2023 年所有参与过 KubeSphere 社区贡献的成员致以最诚挚的感谢&#xff0c;快来看看有没有你&#xff01; 开源项目发展情况 2023 年&#…

go实现判断20000数据范围内哪些是素数(只能被1和它本身整除的数),采用多协程和管道实现

实现一个并发程序&#xff0c;用于寻找 20000 以内的所有素数。使用了 Goroutines 和 Channels 来分发和处理任务&#xff0c;并通过 WaitGroup&#xff08;实现为 exitChan&#xff09;来同步 Goroutines 的退出。 一.GO代码 package mainimport ("fmt""time…

Python-基础篇-类与对象/面向对象程序设计-py脚本

面向对象基础 第一个面向对象 class Cat:def eat(self):print("小猫爱吃鱼")def drink(self):print("小猫要喝水")# 创建猫对象 tom Cat()tom.eat() tom.drink()print(tom)addr id(tom) print("%x" % addr)新建两个猫对象 class Cat:def ea…

RecyclerView事件拦截

本文主要分析RecyclerView的onInterceptTouchEvent()对三种事件怎么处理的 这里只放了一些比较重要点的代码&#xff0c;有一部分省略 先看onInterceptTouchEvent()的返回值 return mScrollState SCROLL_STATE_DRAGGING解释一下mScrollState这个变量&#xff0c;代表Recycle…

NAT实验

一&#xff1a;实验要求 二&#xff1a;实验分析 拓扑图 三&#xff1a;实验配置 1&#xff1a;路由器配置 R1配置IP R2配置IP 2&#xff1a;缺省路由 查看路由表 3&#xff1a;端口映射 4&#xff1a;pc、HTTP配置 5:DNS、client配置 四&#xff1a;实验结果 pc可以ping…

css height:单位 % 高度 跟vh高度区别

css height:单位 % 高度 跟vh高度区别 以下内容来自&#xff1a;chatgpt中文网动态生成 vh 和百分比单位&#xff08;%&#xff09;都是相对长度单位&#xff0c;但它们相对的基准不同。vh&#xff08;视窗高度&#xff09;&#xff1a; 表示相对于视窗高度的百分比。1vh 等于…

三菱plc学习入门(创建属于自己的FB模块)

在现实生活中&#xff0c;往往会需要修改一些属于方便自己的库&#xff0c;1&#xff0c;自己创建的库方便自己使用与查看2&#xff0c;提高自己编程能力&#xff0c;3&#xff0c;保护自己的程序不被外人修改&#xff01;&#xff01;&#xff01;下面就让我来操作一下 导入需…

「HDLBits题解」Karnaugh Map to Circuit

本专栏的目的是分享可以通过HDLBits仿真的Verilog代码 以提供参考 各位可同时参考我的代码和官方题解代码 或许会有所收益 相关资料&#xff1a;卡诺图化简法-CSDN博客 题目链接&#xff1a;Kmap1 - HDLBits module top_module(input a,input b,input c,output out );assig…