详解StringBuilder和StringBuffer(区别,使用方法,含源码讲解)

news2025/1/18 16:57:39


目录

一.为什么要使用StringBuilder和StringBuffer

字符串的不可变性

性能损耗

二.StringBuilder和StringBuffer

StringBuffer源码讲解

使用方式

三.常用方法总结

示例: 

四.StringBuilder和StringBuffer的区别


一.为什么要使用StringBuilder和StringBuffer

在引入StringBuilder和StringBuffer之前,我们可以回顾一下之前我们对于字符串的拼接操作,大多都是如下直接进行拼接:

    public static void main(String[] args) {
        String s = "hello";
        s += " world";
        System.out.println(s); // 输出:hello world
    }

 这样的操作固然是没有问题的,但是如果要说到效率的话,这样的代码效率就非常的低下了,为什么低下呢?说到这里我们就要提到字符串的相关性质了。

字符串的不可变性

String类在设计的时候就是不可改变的,我们可以在JDK1.8的源码中看见如下的注释

因此,我们平常使用的对于String字符串操作的方法,都是新建了一个对象来进行操作,想验证这个结论也很简单,我们随便选择一个方法,我们使用 “ == ” 相当于比较的是俩边变量的地址的哈希值,我们将一个字符串和对它进行大写转换后的字符串进行对比

    public static void main(String[] args) {
        String s = "hello";
        //s.toUpperCase(Locale.of(s));
        System.out.println( s == s.toUpperCase(Locale.of(s)));
    }

 输出结果:

性能损耗

我们再回顾刚才对于字符串的拼接操作,每一次拼接都要新建一个对象的, 当拼接次数非常多的时候,会造成非常严重的性能问题,我们当然也可以验证这个性能问题,使用 currentTimeMillis 方法可以直接拿到当前时刻系统的时间戳,我们可以通过一个循环来展示一下使用传统方式拼接字符串的方式会有怎么样的一个性能损耗

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        String s = " ";
        for(int i = 0; i < 10000; ++i){
            s += i;
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }

输出结果:

当然这还只是10000次循环就造成了82毫秒的运行时间,实际工程中所需的循环次数往往是不可估摸的,因此使用这种方式进行拼接往往是不能完成我们的性能要求的


二.StringBuilder和StringBuffer

为了解决上述的问题,我们就可以使用StringBuilderStringBuffer来进行字符串的拼接等操作,我们可以打开API来查看什么是StringBuilder和StringBuffer

StringBuilder:

 StringBuffer:

StringBuffer源码讲解

在一般使用的时候,他们的功能大致相同,这里笔者进行讲解就只选取其中一种,整体的包含的方法,使用的技巧大多都是一样的,因此不用担心知识覆盖面不全面,笔者这里就以 StringBuffer 来举例,我们可以在IDEA中打开 StringBuffer 的源码,我们可以发现它也是被 final 修饰,继承了父类 AbstractStringBuilder 并且实现了部分接口

父类 AbstractStringBuilder 中一共俩个成员变量:

我们可以看见它的构造方法包含了不同初始化对应的操作:

使用方式

通过源码中的的super关键字结合和上述父类中的成员变量,我们可以得到以下结论:我们默认新建一个 StringBuffer 的时候实际上是新建了一个16字节的数组,我们也可以使用其他的俩个构造方法在传参的时候直接传入大小参数或者直接传入一个字符串

我们总结三种常用的初始化方式如下:

  • 不传参数,默认16字节大小的数组
  • 传入参数直接申明大小
  • 传入字符串
        StringBuffer stringBuffer1 = new StringBuffer();
        StringBuffer stringBuffer3 = new StringBuffer(20);
        StringBuffer stringBuffer2 = new StringBuffer("hello");

三.常用方法总结

我们的StringBuilderStringBuffer最大的特征就是他们内部是可变的,我们通过这俩个类去操作字符串的时候,可以不用新建一个对象,因此我们在进行字符串的拼接的时候往往都是用的这俩个类进行操作,这极大程度上有利于我们提高程运行的效率

我们再谈文章开始说的那个例子,我们使用StringBuffer中的 append 方法可以直接拼接字符,我们分别使用传统的拼接字符和这里的StringBuffer来对比拼接字符所需要的时间

    public static void main(String[] args) {
        
        long start = System.currentTimeMillis();
        String s = "";
        for(int i = 0; i < 10000; ++i){
            s += i;
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start);

        System.out.println("=======分割行========");

        start = System.currentTimeMillis();
        StringBuffer sbf = new StringBuffer("");
        for(int i = 0; i < 10000; ++i){
            sbf.append(i);
        }
        end = System.currentTimeMillis();
        System.out.println(end - start);

    }

输出结果:

我们可以直观的发现:使用 StringBuffer 来拼接字符比直接拼接的效率提高了几十倍,而如果加多循环次数的话,这个倍数还能继续再增加,将原本程序的效率提高几百倍不是梦

除了上述的appen方法,我们将常用的方法总结如下:

方法说明
StringBuff append(String str)
在尾部追加,相当于 String += ,可以追加: boolean char char[] 、 double、 float int long Object String StringBuff 的变量
char charAt(int index)
获取 index 位置的字符
int length()
获取字符串的长度
int capacity()
获取底层保存字符串空间总的大小
void ensureCapacity(int mininmumCapacity)
扩容
void setCharAt(int index, char ch)
index 位置的字符设置为 ch
int indexOf(String str)
返回 str 第一次出现的位置
int indexOf(String str, int fromIndex)
fromIndex 位置开始查找 str 第一次出现的位置
int lastIndexOf(String str)
返回最后一次出现 str 的位置
int lastIndexOf(String str, int fromIndex)
fromIndex 位置开始找 str 最后一次出现的位置
StringBuff insert(int
offset, String str)
offset 位置插入:八种基类类型 & String 类型 & Object 类型数据
StringBuffer deleteCharAt(int index)
删除 index 位置字符
StringBuffer delete(int start, int end)
删除 [start, end) 区间内的字符
StringBuffer replace(int start, int end, String str)
[start, end) 位置的字符替换为 str
String substring(int start)
start 开始一直到末尾的字符以 String 的方式返回
String substring(int start, int end)
[start, end) 范围内的字符以 String 的方式返回
StringBuffer reverse()
反转字符串
String toString()
将所有字符按照 String 的方式返回

示例: 

    public static void main(String[] args) {

        StringBuilder sb1 = new StringBuilder("hello");
        StringBuilder sb2 = sb1;
        // 追加:即尾插-->字符、字符串、整形数字
        sb1.append(' '); // hello
        sb1.append("world"); // hello world
        sb1.append(123); // hello world123
        System.out.println(sb1); // hello world123
        System.out.println(sb1 == sb2); // true
        System.out.println(sb1.charAt(0)); // 获取0号位上的字符 h
        System.out.println(sb1.length()); // 获取字符串的有效长度14
        System.out.println(sb1.capacity()); // 获取底层数组的总大小
        sb1.setCharAt(0, 'H'); // 设置任意位置的字符 Hello world123
        sb1.insert(0, "Hello world!!!"); // Hello world!!!Hello world123
        System.out.println(sb1);
        System.out.println(sb1.indexOf("Hello")); // 获取Hello第一次出现的位置
        System.out.println(sb1.lastIndexOf("hello")); // 获取hello最后一次出现的位置
        sb1.deleteCharAt(0); // 删除首字符
        sb1.delete(0, 5); // 删除[0, 5)范围内的字符
        String str = sb1.substring(0, 5); // 截取[0, 5)区间中的字符以String的方式返回
        System.out.println(str);
        sb1.reverse(); // 字符串逆转
        str = sb1.toString(); // 将StringBuffer以String的方式返回
        System.out.println(str);
        
    }

从上述例子可以看出:StringStringBuilder最大的区别在于String的内容无法修改,而StringBuilder的内容可以修改,因此频繁修改字符串的情况考虑使用StringBuilder

注意:String和StringBuilder类不能直接转换。如果要想互相转换,可以采用如下原则:

  • String变为StringBuilder: 利用StringBuilder的构造方法append()方法
  • StringBuilder变为String: 调用toString()方法

四.StringBuilder和StringBuffer的区别

我们可以打开StringBuffer的源码,我们观察到几乎每一个StringBuffer的前面都有一个synchronized来修饰StringBuffer,这里的synchronized其实就可以理解为一个锁,被synchronized修饰的方法不允许同时被多个对象在同一时刻调用,这样的设立是为了多线程的程序的安全性。

举个通俗的例子:现在有小王,小李,小红三个人想上厕所,但是厕所只有一个,小王先去上厕所,那么小李或者小红就只能等小王用完厕所出来了后,才能去上厕所

而我们的StringBuffer就是类似这样设置的,当一个对象调用被synchronized修饰的方法的时候,这个方法就会被上锁,其他对象不能使用,只有当前这个对象使用完这个方法之后,也就是解锁之后,其他对象才能访问

当我们打开StringBuilder的源码会发现我们的StringBuilder并没有这样的设置操作

总结:

也就是说StringBuffer是为了多线程的安全,但是频繁的上锁解锁会降低代码的运行效率,而StringBuilder虽然没有安全性的考虑,但是它不用开锁解锁,所以运行效率更高,我们在编程中如果需要安全性就使用StringBuffer,如果是为了高效率就使用StringBuilder




 本次的分享就到此为止了,希望我的分享能给您带来帮助,也欢迎大家三连支持,你们的点赞就是博主更新最大的动力!如有不同意见,欢迎评论区积极讨论交流,让我们一起学习进步!有相关问题也可以私信博主,评论区和私信都会认真查看的,我们下次再见

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

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

相关文章

电动汽车充放电V2G模型MATLAB代码

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 主要内容&#xff1a; 本程序主要建立电动汽车充放电V2G模型&#xff0c;采用粒子群算法&#xff0c;在保证电动汽车用户出行需求的前提下&#xff0c;为了使工作区域电动汽车尽可能多的消纳供给商场基础负荷…

Java进阶——多线程相关,实际应用中的积累,持续更新

目录 多线程相关CountDownLatch赛跑的案例countDownLatch.await(300, TimeUnit.SECONDS); Java其他进阶Map的put方法只放一个元素的集合 多线程相关 CountDownLatch 案例&#xff1a;主线程的执行需要等待子线程执行完&#xff0c;等各个线程执行完毕后&#xff0c;主线程做收…

【图像分类】基于深度学习的垃圾分类系统的设计与实现(ResNet网络,附代码和数据集)

写在前面: 首先感谢兄弟们的关注和订阅,让我有创作的动力,在创作过程我会尽最大能力,保证作品的质量,如果有问题,可以私信我,让我们携手共进,共创辉煌。(专栏订阅用户订阅专栏后免费提供数据集和源码一份,超级VIP用户不在服务范围之内,不想订阅专栏的兄弟们可以私信…

万字解析:十大排序(直接插入排序+希尔排序+选择排序+堆排序+冒泡排序+快速排序+归并排序+计数排序+基数排序+桶排序)

文章目录 十大排序排序算法复杂度及稳定性分析一、 排序的概念1.排序&#xff1a;2.稳定性&#xff1a;3.内部排序&#xff1a;4.外部排序&#xff1a; 二、插入排序1.直接插入排序2.希尔排序 三、选择排序1.直接选择排序方法一方法二直接插入排序和直接排序的区别 2.堆排序 四…

SpringBoot集成Swagger2登录功能和安全认证

本篇文章要实现的功能&#xff1a; 1.集成swagger2.集成swagger登录功能&#xff0c;访问 /swagger-ui.html需要先登录3.集成安全认证&#xff0c;访问接口时携带header 请求接口时携带了上一步输入的header参数和值 1.集成swagger jdk11&#xff0c;SpringBoot 2.7.13 pom…

redis运维(十四) hash缓存案例

一 缓存案例 ① 需求 ② 个人理解 策略&#xff1a;不更新缓存&#xff0c;而是删除缓存大部分观点认为&#xff1a;1、做缓存不应该是去更新缓存,而是应该删除缓存2、然后由下个请求去缓存,发现不存在后再读取数据库,写入redis缓存 高并发场景下,到底先更新缓存还是先更…

Android studio 迁移之后打开没反应

把Android studio由d盘迁移到c盘&#xff0c;点击没反应&#xff1b; 需要把C:\Users\xxxx\AppData\Roaming\Google\AndroidStudio2022.3 目录下的studio64.exe.vmoptions 修改为C:&#xff0c;删除该文件会导致无法安装app。 里面配置了一个

git常用命令(git github ssh)

目录 1、语法说明2、本地仓库相关操作建立一个git文件(git init)把工作区的文件添加到暂存区(git add)把暂存区的文件添加到本地仓库(git commit)查看暂存区和本地仓库中的文件(git ls-files)查看文件夹下所有文件的状态(git status)查看版本库中的提交记录(git log)恢复的文件…

Linux系统管理与服务器安全:构建稳健云数据中心

&#x1f482; 个人网站:【 海拥】【神级代码资源网站】【办公神器】&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交流的小伙伴&#xff0c;请点击【全栈技术交流群】 在当今数字化时代&#xff0c;云数据中心已经成…

Program Header Table(转载)

程序头表与段表相互独立&#xff0c;由ELF文件头统一管理。 程序头表负责ELF文件从文件到加载后映像的映射关系&#xff0c;一般只有可执行文件包含。 1. segment和section segment: 程序头表项描述的对象称为segment&#xff0c;即elf文件加载后的数据块&#xff1b; 它提供…

【计算方法与科学建模】矩阵特征值与特征向量的计算(二):Jacobi 过关法及其Python实现(Jacobi 旋转法的改进)

文章目录 一、Jacobi 旋转法1. 基本思想2. 注意事项 二、Jacobi 过关法1. 基本思想2. 注意事项 三、Python实现迭代过程&#xff08;调试&#xff09; 矩阵的特征值&#xff08;eigenvalue&#xff09;和特征向量&#xff08;eigenvector&#xff09;在很多应用中都具有重要的数…

2023.11.22 homework

七年级数学 五年级数学 也不知道可以教到几年级&#xff0c;估计很快就教不动了。人生啊。

地图导航测试用例,你get了吗?

地图导航是我们经常使用的工具&#xff0c;能帮助我们指引前进的方向。 接下来&#xff0c;会从功能测试、UI测试、兼容测试、安全测试、网络测试、性能测试、易用性测试、文档和国际化语言测试8个方面来编写地图导航测试用例。 一 功能测试 输入起点和终点&#xff0c;验证…

五大资源之Service(可以固定IP)

Service可以看作是一组同类Pod对外访问接口,借助Service应用可以方便的实现服务发现与负载均衡 创建集群内部可以访问Service #暴露Service(也创建在了namespace dev下) [root@master ~]# kubectl expose deployment(pod控制器) nginx --name=svc-nginx1 --type=Cluste…

实在智能携“TARS大模型”入选“2023中国数据智能产业AI大模型先锋企业”

近日&#xff0c;由数据猿与上海大数据联盟联合主办的“2023企业数智化转型升级发展论坛”在上海圆满收官。 论坛颁奖典礼上&#xff0c;《2023中国数据智能产业AI大模型先锋企业》等六大榜单正式揭晓&#xff0c;旨在表彰在AI领域为数智化升级取得卓越成就和突出贡献的企业&am…

最新PHP熊猫头图片表情斗图生成源码

这是一款能生成熊猫头表情斗图的自适应系统源码&#xff0c;无论是在电脑还是手机上都可以正常使用&#xff01;这个源码集成了搜狗搜索图片接口&#xff0c;可以轻松地一键搜索数百万张图片&#xff0c;并且还包含了表情制作等功能模块。对于一些新站来说&#xff0c;这是一个…

代码规范之-理解ESLint、Prettier、EditorConfig

前言 团队多人协同开发项目&#xff0c;困扰团队管理的一个很大的问题就是&#xff1a;无可避免地会出现每个开发者编码习惯不同、代码风格迥异&#xff0c;为了代码高可用、可维护性&#xff0c;需要从项目管理上尽量统一和规范代码。理想的方式需要在项目工程化方面&#xff…

元素清空操作clear与选择操作check

元素清空操作clear与选择操作check clear() 作用 清空输入框的所有内容.clear() 等价于 .type("{selectall}{backspace}") 语法 .clear() .clear(options)option选项 元素选中操作check与uncheck check 语法 // 所有匹配到的选择框都会被选中一遍 .check()/…

【HarmonyOS】元服务卡片本地启动拉起加桌没问题,上架后拉起加桌时卡片展示异常

【关键字】 加桌选卡展示异常 、 2卡共用一个布局 、 代码混淆 【问题现象】 元服务卡片在本地启动拉起加桌时&#xff0c;多卡的选卡过程显示是没问题的。但是在上架后拉起加桌时&#xff0c;多卡的选卡过程卡片展示异常。 代码逻辑是通过创建卡片的时候判断卡片的尺寸大小…