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


目录

一.为什么要使用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

为了解决上述的问题,我们就可以使用StringBuilder和StringBuffer来进行字符串的拼接等操作,我们可以打开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");

三.常用方法总结

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

我们再谈文章开始说的那个例子,我们使用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(' '); // hellosb1.append("world"); // hello worldsb1.append(123); // hello world123System.out.println(sb1); // hello world123System.out.println(sb1 == sb2); // trueSystem.out.println(sb1.charAt(0)); // 获取0号位上的字符 hSystem.out.println(sb1.length()); // 获取字符串的有效长度14System.out.println(sb1.capacity()); // 获取底层数组的总大小sb1.setCharAt(0, 'H'); // 设置任意位置的字符 Hello world123sb1.insert(0, "Hello world!!!"); // Hello world!!!Hello world123System.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);}

从上述例子可以看出:String和StringBuilder最大的区别在于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.tpcf.cn/news/160265.html

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

相关文章

C语言--每日五道选择题-- Day22

第一题&#xff08;注意&#xff09; 1.下列 C 代码中&#xff0c;不属于未定义行为的有&#xff1a;______。 A&#xff1a;int i0; i(i); B&#xff1a;char *p"hello"; p[1]E; C&#xff1a;char *p"hello"; char ch*p; D&#xff1a;int i0; printf(&q…

Redis7--基础篇3(持久化)

持久化介绍 官网地址&#xff1a; https://redis.io/docs/manual/persistence RDB(Redis DataBase)AOF(Append Only File)RDB AOF RDB模式(Redis DataBase) RDB 持久性以指定的时间间隔执行数据集的时间点快照。 实现类似照片记录效果的方式&#xff0c;就是把某一时刻的数据…

计算机组成原理(万字爆肝整理)

第一章 计算机系统概述 “较简单&#xff0c;不做过多赘述&#xff0c;后面会详细学到” 第一节 计算机系统层次结构 1.计算机系统的基本组成&#xff1a;硬件软件 2.计算机硬件的基本组成&#xff1a;运算器存储器控制器输入设备输出设备 3.系统软件和应用软件 系统软件…

expdp导出分区表缓慢排查(Streams AQ: waiting for messages in the queue )

基本信息 单机&#xff0c;从老环境迁移到19.19。之前的导出速度接受范围内。硬件是提升的 导出使用了压缩&#xff0c;加密&#xff0c;并行64进程&#xff0c;表分区约90个&#xff0c;无lob字段。 现象 导出开始时能并行导出&#xff08;并行约45个&#xff0c;没起到64…

Cypress环境变量

Cypress环境变量 baseUrl 当你配置了 baseUrl &#xff0c;测试套件中的 cy.visit() 、 cy.request() 都会自动以 baseUrl 的值作为前缀并且&#xff0c;当你需要访问某些网址或者发起接口请求时&#xff0c;在代码中就可以不用再指定请求的 host 或者 url 了 如何配置 base…

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

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

redis的高可用(主从复制和哨兵模式)

redis的高可用&#xff08;主从复制和哨兵模式&#xff09; redis的性能管理&#xff1a;redis的数据缓存在内存当中 INFO memory&#xff1a;查看redis内存使用情况 used_memory:1800800&#xff1a;redis中数据占用的内存 used_memory_rss:5783552&#xff1a;redis向操作…

Halcon Solution Guide I basics(3): Region Of Interest(有兴趣区域/找重点)

文章目录 文章专栏前言文章解读前言创建ROI案例1&#xff1a;直接截取ROI手动截取ROI 总结ROI套路获取窗口句柄截取ROI区域获取有效区域 Stop组合 文章专栏 Halcon开发 Halcon学习 练习项目gitee仓库 CSDN Major 博主Halcon文章推荐 前言 今天来看第三章内容&#xff0c;既然是…

QTableWidget——编辑单元格

文章目录 前言熟悉QTableWiget&#xff0c;通过实现单元格的合并、拆分、通过编辑界面实现表格内容及属性的配置、实现表格的粘贴复制功能熟悉QTableWiget的属性 一、[单元格的合并、拆分](https://blog.csdn.net/qq_15672897/article/details/134476530?spm1001.2014.3001.55…

Docker实践笔记7:构建MySQL 8镜像

使用Docker构建MySQL 8镜像并运行容器 本教程将指导您使用Dockerfile构建和运行一个MySQL 8容器。让我们开始吧&#xff01; 步骤1&#xff1a;创建Dockerfile 在您的项目根目录下创建一个名为Dockerfile的文件。以下是Dockerfile的示例内容&#xff1a; # 基于最新的MySQL…

docker、elasticsearch8、springboot3集成备忘

目录 一、背景 二、安装docker 三、下载安装elasticsearch 四、下载安装elasticsearch-head 五、springboot集成elasticsearch 一、背景 前两年研究了一段时间elasticsearch&#xff0c;当时也是网上找了很多资料&#xff0c;最后解决个各种问题可以在springboot上运行了…

vue-使用input封装上传文件图片全局组件

前言 实际开发过程中&#xff0c;我们经常遇见需要上传文件图片功能&#xff0c;可以封装一个全局组件来调用 原理很简单&#xff0c;首先获取到文件或图片对象&#xff0c;调用自己公司文档服务器的接口&#xff0c;上传文件图片 为了方便用户体验&#xff0c;我们应该在上传…

Godot

前言 为什么要研究开源引擎 主要原因有&#xff1a; 可以享受“信创”政策的红利&#xff0c;非常有利于承接政府项目。中美脱钩背景下&#xff0c;国家提出了“信创”政策。这个政策的核心就是&#xff0c;核心技术上自主可控。涉及的产业包括&#xff1a;芯片、操作系统、数据…

【Django使用】md文档10大模块第5期:Django数据库增删改查和Django视图

Django的主要目的是简便、快速的开发数据库驱动的网站。它强调代码复用&#xff0c;多个组件可以很方便的以"插件"形式服务于整个框架&#xff0c;Django有许多功能强大的第三方插件&#xff0c;你甚至可以很方便的开发出自己的工具包。这使得Django具有很强的可扩展…

Vue项目 配置项设置

一、项目运行时浏览器自动打开 找到package.json文件 找到"sctipts"配置项 在"serve"配置项最后加上--open "scripts": {"serve": "vue-cli-service serve --open","build": "vue-cli-service build&quo…

Redis面试内容,Redis过期策略,Redis持久化方式,缓存穿透、缓存击穿和缓存雪崩,以及解决办法

文章目录 一、redis什么是RedisRedis使用场景1、缓存2、数据共享[分布式](https://so.csdn.net/so/search?q分布式&spm1001.2101.3001.7020)3、分布式锁4、全局ID5、计数器6、限流7、位统计 Redis有5中数据类型&#xff1a; SSHLZRedis中一个key的值每天12点过期&#xff…

Cookie、Session、CBV加装饰器的三种方法

【0】cookie、session和Token的发展史 【1】Cookie的形式 存储形式&#xff1a;k&#xff1a;v键值对存储位置&#xff1a;客户端缺点&#xff1a;不安全&#xff0c;信息可能会泄露 【2】session的形式 标识符&#xff0c;表示我是当前用户加密出来的数据对敏感信息进行加密…

排序算法-----快速排序(非递归实现)

目录 前言 快速排序 基本思路 非递归代码实现 前言 很久没跟新数据结构与算法这一栏了&#xff0c;因为数据结构与算法基本上都发布完了&#xff0c;哈哈&#xff0c;那今天我就把前面排序算法那一块的快速排序完善一下&#xff0c;前面只发布了快速排序递归算法&#xff0c;…

单链表相关面试题--3.链表的中间节点

3.链表的中间节点 876. 链表的中间结点 - 力扣&#xff08;LeetCode&#xff09; /* 解题思路&#xff1a; 通过快慢指针找到中间节点&#xff0c;快指针每次走两步&#xff0c;慢指针每次走一步&#xff0c;当快指针走到结尾的时候&#xff0c;慢指针正好走到中间位置 */ typ…

HTTPS协议的加密流程

目录 一&#xff0c;HTTPS是什么 二&#xff0c;两种加密方式 三&#xff0c;HTTPS的加密过程 3.1 引入对称加密 3.2 引入非对称加密 3.3 引入证书 一&#xff0c;HTTPS是什么 HTTPS也是一个应用层协议&#xff0c;它是在HTTP协议的基础上引入了一个加密层。因为HTTP协议…