Java實(shí)現(xiàn)字符串拆分的三種方法詳解
字符串拆分,看似簡(jiǎn)單的操作,背后卻隱藏著性能的玄機(jī)。今天,我們邀請(qǐng)三位頂尖高手同臺(tái)競(jìng)技:String.split、StringUtils.splitByWholeSeparatorPreserveAllTokens、StringTokenizer。誰才是你項(xiàng)目中的最佳拍檔?
第一章:三大高手簡(jiǎn)介
| 選手 | 門派 | 武功特點(diǎn) | 登場(chǎng)年代 |
|---|---|---|---|
| String.split | JDK名門 | 正則心法,功能強(qiáng)大 | JDK 1.4 |
| StringUtils.splitByWholeSeparatorPreserveAllTokens | Apache Commons | 內(nèi)外兼修,功能與性能并重 | |
| StringTokenizer | JDK元老 | 老而彌堅(jiān),簡(jiǎn)單純粹 | JDK 1.0 |
第二章:巔峰對(duì)決——性能篇
2.1 性能基準(zhǔn)測(cè)試
測(cè)試環(huán)境:JMH基準(zhǔn)測(cè)試,拆分100萬次,字符串長(zhǎng)度100,10個(gè)字段
| 選手 | 吞吐量 (ops/ms) | 相對(duì)性能 | 內(nèi)存分配 |
|---|---|---|---|
| StringTokenizer | 8,200 | 96% | 極少 |
| StringUtils.splitByWholeSeparatorPreserveAllTokens | 7,800 | 92% | 中等 |
| String.split | 2,800 | 33% | 較多 |
核心結(jié)論:
- StringTokenizer:性能之王,當(dāng)之無愧的冠軍
- StringUtils:雖為第三方庫,性能卻遠(yuǎn)超JDK原生split
- String.split:慘遭墊底,僅為冠軍1/3的性能
2.2 為什么String.split這么慢?
關(guān)鍵原因:每次調(diào)用都要編譯正則表達(dá)式!
// 分解String.split的成本
str.split(",") 的實(shí)際開銷:
1. Pattern.compile(",") // 編譯正則,創(chuàng)建Pattern對(duì)象
2. Pattern.compile(...).split(str) // 執(zhí)行拆分
3. 創(chuàng)建臨時(shí)數(shù)組 // 內(nèi)存分配
4. 正則引擎匹配 // 復(fù)雜的狀態(tài)機(jī)遍歷
JDK底層對(duì)單字符做了特殊處理,不用正則引擎,而使用indexOf,單字符性能顯著提升
第三章:巔峰對(duì)決——功能篇
3.1 行為對(duì)比測(cè)試
測(cè)試1:連續(xù)分隔符的處理
String str = "a,,b,c"; // 兩個(gè)逗號(hào)之間是空字符串
// String.split
System.out.println(Arrays.toString(str.split(",")));
// 輸出: [a, , b, c] ? 默認(rèn)保留中間空
// StringUtils.splitByWholeSeparatorPreserveAllTokens
System.out.println(Arrays.toString(
StringUtils.splitByWholeSeparatorPreserveAllTokens(str, ",")));
// 輸出: [a, , b, c] ? 保留所有空
// StringTokenizer
StringTokenizer st = new StringTokenizer(str, ",");
while(st.hasMoreTokens()) System.out.print(st.nextToken() + " ");
// 輸出: a b c ? 空字符串被直接跳過了!
結(jié)論:StringTokenizer默認(rèn)不返回空標(biāo)記,這是它與眾不同的地方。
測(cè)試2:末尾分隔符的處理
String str = "a,b,c,"; // 末尾有逗號(hào)
// String.split (默認(rèn))
System.out.println(Arrays.toString(str.split(",")));
// 輸出: [a, b, c] ? 末尾空被丟棄
// String.split (保留末尾空)
System.out.println(Arrays.toString(str.split(",", -1)));
// 輸出: [a, b, c, ] ? 用limit=-1保留
// StringUtils.splitByWholeSeparatorPreserveAllTokens
System.out.println(Arrays.toString(
StringUtils.splitByWholeSeparatorPreserveAllTokens(str, ",")));
// 輸出: [a, b, c, ] ? 默認(rèn)保留
// StringTokenizer
StringTokenizer st = new StringTokenizer(str, ",");
while(st.hasMoreTokens()) System.out.print(st.nextToken() + " ");
// 輸出: a b c ? 末尾空被忽略
測(cè)試3:特殊分隔符的處理
String str = "a.b.c";
// String.split (需要轉(zhuǎn)義)
System.out.println(Arrays.toString(str.split("\\.")));
// 輸出: [a, b, c] ? 但必須記得轉(zhuǎn)義
// String.split (忘了轉(zhuǎn)義)
System.out.println(Arrays.toString(str.split(".")));
// 輸出: [] ? 空數(shù)組!因?yàn)?."匹配任意字符
// StringUtils.splitByWholeSeparatorPreserveAllTokens
System.out.println(Arrays.toString(
StringUtils.splitByWholeSeparatorPreserveAllTokens(str, ".")));
// 輸出: [a, b, c] ? 不用轉(zhuǎn)義,直接使用
// StringTokenizer
StringTokenizer st = new StringTokenizer(str, ".");
while(st.hasMoreTokens()) System.out.print(st.nextToken() + " ");
// 輸出: a b c ? 也不用轉(zhuǎn)義
測(cè)試4:多字符分隔符
String str = "a--b--c";
// String.split
System.out.println(Arrays.toString(str.split("--")));
// 輸出: [a, b, c] ? 支持,但"--"是正則,特殊場(chǎng)景需要轉(zhuǎn)義
// StringUtils.splitByWholeSeparatorPreserveAllTokens
System.out.println(Arrays.toString(
StringUtils.splitByWholeSeparatorPreserveAllTokens(str, "--")));
// 輸出: [a, b, c] ? 原生支持多字符,且不涉及正則
// StringTokenizer
StringTokenizer st = new StringTokenizer(str, "--");
while(st.hasMoreTokens()) System.out.print(st.nextToken() + " ");
// 輸出: a b c ? 注意:這是按'-'拆分,不是按"--"!
3.2 功能對(duì)比總表
| 特性 | String.split | StringUtils.xxx | StringTokenizer |
|---|---|---|---|
| 保留中間空標(biāo)記 | ? 支持 | ? 支持 | ? 不支持 |
| 保留末尾空標(biāo)記 | 需limit=-1 | ? 默認(rèn)保留 | ? 不支持 |
| 正則表達(dá)式支持 | ? 強(qiáng)大 | ? 不支持 | ? 不支持 |
| 多字符分隔符 | ? 支持(正則) | ? 原生支持 | ? 視為字符集 |
| 特殊字符轉(zhuǎn)義 | 必須轉(zhuǎn)義 | 無需轉(zhuǎn)義 | 無需轉(zhuǎn)義 |
| null輸入 | 拋NPE | 返回null | 拋NPE |
| 返回類型 | String[] | String[] | Enumeration |
第四章:終極總結(jié)
4.1 三大高手定位
| 選手 | 定位 | 適用場(chǎng)景 | 不適用場(chǎng)景 |
|---|---|---|---|
| String.split | 全能選手 | 日常開發(fā)、需要正則 | 性能敏感 |
| StringUtils.xxx | 平衡大師 | 需要保留空標(biāo)記、多字符分隔符 | 正則表達(dá)式匹配 |
| StringTokenizer | 元老級(jí) | 大文件處理、遺留系統(tǒng) | api陳舊,功能匱乏,不在推薦使用 |
4.2 如何選擇
如果你不需要正則,大多數(shù)場(chǎng)景直接用:StringUtils
String[] result = StringUtils.splitByWholeSeparatorPreserveAllTokens(str, ",");
如果你需要正則能力:String.split 或 Pattern
String[] result = str.split("\\s+"); // 按空白符拆分
如果你僅處理很簡(jiǎn)單的文本,追求極致性能:StringTokenizer
StringTokenizer st = new StringTokenizer(str, ",");
4.3 面試官最愛問的問題
Q1:StringUtils.splitByWholeSeparatorPreserveAllTokens為什么比String.split快?
A:因?yàn)樗褂闷胀ㄗ址檎遥╥ndexOf),不涉及正則表達(dá)式,避免了編譯開銷。同時(shí)它一次性分配內(nèi)存,不像StringTokenizer那樣懶加載。
Q2:StringTokenizer現(xiàn)在還推薦使用嗎?
A:除非你在維護(hù)遺留系統(tǒng),或者明確需要它"不返回空標(biāo)記"的特性,否則不建議在新代碼中使用。它的API太古老,不支持現(xiàn)代Java特性,而且行為與主流方法不一致。
Q3:如果我要解析1GB的日志文件,該用哪個(gè)?
A:StringTokenizer或手寫indexOf。因?yàn)樗鼈兌际橇魇教幚?,不?huì)一次性創(chuàng)建大數(shù)組。String.split會(huì)創(chuàng)建一個(gè)大數(shù)組,可能導(dǎo)致內(nèi)存溢出。
結(jié)束語
字符串拆分,看似簡(jiǎn)單,實(shí)則暗藏玄機(jī)。四大高手各有所長(zhǎng):
- String.split:名門正派,能力全面
- StringUtils:內(nèi)外兼修,平衡之道
- StringTokenizer:老而彌堅(jiān),返璞歸真
記住:沒有絕對(duì)的最好,只有最合適。理解了它們各自的優(yōu)勢(shì)和局限,你就能在任何場(chǎng)景下做出明智的決策。
到此這篇關(guān)于Java實(shí)現(xiàn)字符串拆分的三種方法詳解的文章就介紹到這了,更多相關(guān)Java字符串拆分內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java創(chuàng)建型設(shè)計(jì)模式之建造者模式詳解
建造者模式是Java中一種創(chuàng)建型設(shè)計(jì)模式,它的主要目的是將一個(gè)復(fù)雜對(duì)象的構(gòu)建過程分解為多個(gè)簡(jiǎn)單對(duì)象的構(gòu)建過程,本文將詳細(xì)介紹Java中的建造者模式,包括它的定義、結(jié)構(gòu)、實(shí)現(xiàn)方法以及應(yīng)用場(chǎng)景等方面,希望對(duì)大家有所幫助2023-05-05
SpringBoot集成P6Spy實(shí)現(xiàn)SQL日志的記錄詳解
P6Spy是一個(gè)框架,它可以無縫地?cái)r截和記錄數(shù)據(jù)庫活動(dòng),而無需更改現(xiàn)有應(yīng)用程序的代碼。一般我們使用的比較多的是使用p6spy打印我們最后執(zhí)行的sql語句2022-11-11
java 線程方法join簡(jiǎn)單用法實(shí)例總結(jié)
這篇文章主要介紹了java 線程方法join簡(jiǎn)單用法,結(jié)合實(shí)例形式總結(jié)分析了Java線程join方法的功能、原理及使用技巧,需要的朋友可以參考下2019-11-11
SpringBoot切面實(shí)現(xiàn)token權(quán)限校驗(yàn)詳解
這篇文章主要介紹了SpringBoot切面實(shí)現(xiàn)token權(quán)限校驗(yàn)詳解,要實(shí)現(xiàn)權(quán)限校驗(yàn),首先數(shù)據(jù)表和實(shí)體類上需要有權(quán)限字段,我的表中permission和gender是通過外鍵約束permission表和gender表實(shí)現(xiàn)枚舉的,因?yàn)榭赏卣剐愿?需要的朋友可以參考下2024-01-01
劍指Offer之Java算法習(xí)題精講鏈表與字符串及數(shù)組
跟著思路走,之后從簡(jiǎn)單題入手,反復(fù)去看,做過之后可能會(huì)忘記,之后再做一次,記不住就反復(fù)做,反復(fù)尋求思路和規(guī)律,慢慢積累就會(huì)發(fā)現(xiàn)質(zhì)的變化2022-03-03
Java AOP實(shí)現(xiàn)自定義滑動(dòng)窗口限流器方法詳解
這篇文章主要介紹了Java AOP實(shí)現(xiàn)自定義滑動(dòng)窗口限流器方法,其中滑動(dòng)窗口算法彌補(bǔ)了計(jì)數(shù)器算法的不足,滑動(dòng)窗口算法把間隔時(shí)間劃分成更小的粒度,當(dāng)更小粒度的時(shí)間間隔過去后,把過去的間隔請(qǐng)求數(shù)減掉,再補(bǔ)充一個(gè)空的時(shí)間間隔,需要的朋友可以參考下2022-07-07

