詳解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(' '); // 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);}

從上述例子可以看出: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.pswp.cn/news/160265.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/160265.shtml
英文地址,請注明出處:http://en.pswp.cn/news/160265.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋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協議…