详解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

为了解决上述的问题,我们就可以使用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




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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++多线程学习(二):多线程通信和锁

参考引用 C11 14 17 20 多线程从原理到线程池实战代码运行环境&#xff1a;Visual Studio 2019 1. 多线程状态 1.1 线程状态说明 初始化 (lnit)&#xff1a;该线程正在被创建就绪 (Ready)&#xff1a;该线程在就绪列表中&#xff0c;等待 CPU 调度运行 (Running)&#xff1a;…

实验7设计建模工具的使用(三)

二&#xff0c;实验内容与步骤 1. 百度搜索1-2张状态图&#xff0c;请重新绘制它们&#xff0c;并回答以下问题&#xff1a; 1&#xff09;有哪些状态&#xff1b; 2&#xff09;简要描述该图所表达的含义&#xff1b; 要求&#xff1a;所绘制的图不得与本文中其它习题一样…

电磁优化的并行空间映射方法

空间映射(SM)是一种公认的加速电磁优化的方法。现有的SM方法大多基于顺序计算机制。本文提出了一种用于电磁优化的并行SM方法。在该方法中&#xff0c;每次迭代开发的代理模型被训练以同时匹配多个点的精细模型。多点训练和SM使代理模型在比标准SM更大的邻域内有效。本文提出的…

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

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

MySQL数据库入门到大牛_基础_12_MySQL数据类型精讲

文章目录 1. MySQL中的数据类型2. 整数类型2.1 类型介绍2.2 可选属性2.2.1 M2.2.2 UNSIGNED2.2.3 ZEROFILL 2.3 适用场景2.4 如何选择&#xff1f; 3. 浮点类型3.1 类型介绍3.2 数据精度说明3.3 精度误差说明 4. 定点数类型4.1 类型介绍4.2 开发中经验 5. 位类型&#xff1a;BI…

PyTorch 之 Dataset 类入门学习

PyTorch 之 Dataset 类入门学习 Dataset 类简介 PyTorch 中的 Dataset 类是一个抽象类&#xff0c;用来表示数据集。通过继承 Dataset 类可以进行自定义数据集的格式、大小和其它属性&#xff0c;供后续使用&#xff1b; 可以看到官方封装好的数据集也是直接或间接的继承自 …

《微信小程序案例大全》大学生期末大作业可以直接使用!!

前言 在大学生活中&#xff0c;期末大作业是锻炼和展示自己所学知识的重要时刻。微信小程序作为一种快速、便捷的应用开发方式&#xff0c;成为了大学生开发实践的热门选择。本文将为大家推荐一系列可以直接使用的微信小程序案例&#xff0c;包括仿真社交、图书管理、学习工具…

智慧城市内涝积水监测仪功能,提升城市预防功能

内涝积水监测仪不仅改变了人们应对城市内涝的老办法&#xff0c;还让智慧城市往前迈了一大步。这个监测仪是怎么做到的呢&#xff1f;就是靠它精准的数据监测和预警&#xff0c;让城市管理有了更科学高效的解决妙招。它就像有了个聪明又负责任的助手&#xff0c;让城市管理更加…

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

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

不到十个例题带你拿下c++双指针算法(leetcode)

移动零问题 https://leetcode.cn/problems/move-zeroes/submissions/ 1.题目解析 必须在原数组进行修改&#xff0c;不可以新建一个数组 非零元素相对顺序不变 2.算法原理 【数组划分】【数组分块】 这一类题会给我们一个数组&#xff0c;让我们划分区间&#xff0c;比如…

C++虚析构和纯虚析构解决delete堆区父类指针无法调用子类的构造函数

#include<iostream> #include<string>using namespace std;//虚析构和纯虚析构 class Animal { public:Animal(){cout<<"执行Animal的构造函数"<<endl;}~Animal(){cout<<"执行Animal的析构函数"<<endl;}virtual void …

对接苹果支付退款退单接口

前言 一般而言&#xff0c;我们其实很少对接退款接口&#xff0c;因为退款基本都是商家自己决定后进行操作的&#xff0c;但是苹果比较特殊&#xff0c;用户可以直接向苹果发起退款请求&#xff0c;苹果觉得合理会退给用户&#xff0c;但是目前公司业务还是需要对接这个接口&am…

蓝桥杯每日一题2023.11.22

题目描述 题目分析 由题目知其每个品牌积分一定小于315故直接暴力枚举每个品牌如果符合要求直接输出即可 &#xff08;答案&#xff1a;150&#xff09; #include<bits/stdc.h> using namespace std; int main() {for(int i 1; i < 315; i ){for(int j 1; j <…

【无标题】dp80采集机和机器人通信相关框架总结

采血机器人通信解析相关框架总结: 类似于dp80,将整个过程进行了分解如下: 类似于dp80,将整个过程进行了分解如下: 上位机界面在进行点击操作的时候,先是通信协议的解析,解析后改变采血的控制状态如下: Dp80主要框架解析࿱

层次分析法--可以帮助你做决策的简单算法

作用 层次分析法是一个多指标的评价算法&#xff0c;主要用来在做决策时&#xff0c;给目标的多个影响因子做权重评分。特别是那些需要主观决策的、或者需要用经验判断的决策方案&#xff0c;例如&#xff1a; 买房子&#xff08;主观决策&#xff09;选择旅游地&#xff08;…

RabbitMQ快速入门(简单收发消息)

文章目录 前言一、数据隔离1.用户管理2.virtual host 二、控制台收发1.交换机2.队列3.绑定 三、编程式收发1.依赖和配置2.收发信息 总结 前言 1.了解数据隔离 2.RabbitMQ控制台收发信息 3.SpringBoot整合RabbitMQ收发信息 一、数据隔离 1.用户管理 点击Admin选项卡&#xff0…

zookeeper单机版的搭建

一 zookeeper的搭建 1.1 上传zkjar包 1.2 搭建配置 1.解压压缩包 [rootlocalhost export]# tar -zxvf zookeeper-3.7.0-bin.tar.gz 2.创建data文件夹 [rootlocalhost export]# cd apache-zookeeper-3.7.0-bin/ [rootlocalhost apache-zookeeper-3.7.0-bin]# ls bin conf…

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

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

使用gin 代理 web网页

问web项目的代理&#xff0c;业界常用的方案是nginx做代理&#xff0c;这个是网上最多资料的。 因为我需要做自己的流量转发&#xff0c;也就是所有访问都要经过我的一个流量分发微服务&#xff0c;这和nginx作用冲突了。如果再加个nginx来做第一层方向代理和网页的静态资源代…

Linux学习第45天:Linux 多点电容触摸屏实验(三):难忘记第一次牵你手的温存

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 本章的思维导图如下&#xff1a; 五、tslib移植与使用 通过 tslib 来直观的测试多点电容触摸屏驱动。 1、tslib移植 1&#xff09;、获取tslib源码 git 地址为…