Java9中String的優(yōu)化

String類的實(shí)現(xiàn)方式

在Java 9之前,String類是由char數(shù)組實(shí)現(xiàn)的蟆湖,每個(gè)char占用兩個(gè)字節(jié)的內(nèi)存空間五嫂。而在Java 9中房官,String類引入了一種稱為"Compact Strings"的新實(shí)現(xiàn)方式切距,將字符串的表示方式從char數(shù)組改為byte數(shù)組朽缎,并使用一種編碼方式將Unicode字符映射到一個(gè)或兩個(gè)字節(jié)的表示方式惨远。這種實(shí)現(xiàn)方式可以大大減少內(nèi)存使用谜悟,尤其是對(duì)于包含大量ASCII字符的字符串。

為什么這么說(shuō)呢北秽?JDK9中葡幸,引入了一個(gè)coder標(biāo)識(shí),用來(lái)區(qū)分是普通的拉丁字母還是UTF16字符贺氓。

static final byte LATIN1 = 0;
static final byte UTF16 = 1;

我們?cè)谌粘J褂弥锌赡芎芏嗲闆r下大量使用英文字母蔚叨,較少使用一些中文或者其他復(fù)雜的字符诀紊,這時(shí)候JDK9中的這種優(yōu)化就能有很大的用處问裕,因?yàn)榘凑赵綣DK8的方案抹蚀,無(wú)論是什么內(nèi)容晌端,就統(tǒng)一按照char來(lái)存儲(chǔ)呜象,這樣對(duì)于那些普通的英文字母根本不需要用兩個(gè)字節(jié)的空間斤讥,一個(gè)字節(jié)就夠了急侥,如果涉及到大量的這種純英文字母的字符串执赡,此時(shí)JDK9的存儲(chǔ)上的優(yōu)化就大大體現(xiàn)了出來(lái)尾抑。

String類中的方法

  1. 構(gòu)造方法
  • String(): 創(chuàng)建一個(gè)空字符串歇父。

  • String(char[] value): 創(chuàng)建一個(gè)包含指定字符序列的字符串蒂培。

  • String(byte[] bytes): 使用默認(rèn)字符集解碼指定的字節(jié)數(shù)組,創(chuàng)建一個(gè)新的字符串榜苫。

  • String(String original): 創(chuàng)建一個(gè)與指定字符串內(nèi)容相同的新字符串护戳。

  1. 字符串操作方法
  • charAt(int index): 返回指定位置上的字符。

  • concat(String str): 將指定字符串連接到此字符串的末尾垂睬。

  • substring(int beginIndex): 返回一個(gè)新的字符串媳荒,它是此字符串的子字符串。

  • substring(int beginIndex, int endIndex): 返回一個(gè)新的字符串羔飞,它是此字符串的子字符串肺樟。

  • replace(char oldChar, char newChar): 返回一個(gè)新字符串,它是通過(guò)用新字符替換此字符串中出現(xiàn)的所有舊字符得到的逻淌。

  • replaceAll(String regex, String replacement): 用指定的字符串替換所有匹配給定的正則表達(dá)式的子字符串么伯。

  • trim(): 返回字符串的副本,忽略前導(dǎo)空白和尾部空白卡儒。

  • toLowerCase(): 使用默認(rèn)語(yǔ)言環(huán)境的規(guī)則將此字符串轉(zhuǎn)換為小寫田柔。

  • toUpperCase(): 使用默認(rèn)語(yǔ)言環(huán)境的規(guī)則將此字符串轉(zhuǎn)換為大寫。

  • getBytes(): 使用平臺(tái)默認(rèn)字符集將此字符串編碼為字節(jié)數(shù)組骨望。

  1. 字符串比較方法
  • equals(Object anObject): 將此字符串與指定對(duì)象進(jìn)行比較硬爆。

  • equalsIgnoreCase(String anotherString): 將此字符串與指定字符串進(jìn)行比較,忽略大小寫差異擎鸠。

  • compareTo(String anotherString): 按字典順序比較兩個(gè)字符串缀磕。

  • compareToIgnoreCase(String str): 按字典順序比較兩個(gè)字符串,忽略大小寫差異劣光。

  1. 其他方法
  • length(): 返回此字符串的長(zhǎng)度袜蚕。

  • isEmpty(): 當(dāng)且僅當(dāng)字符串長(zhǎng)度為 0 時(shí)返回 true。

  • valueOf(int i): 返回 int 參數(shù)的字符串表示形式绢涡。

  • join(CharSequence delimiter, CharSequence... elements): 將給定的字符串序列以指定的分隔符拼接起來(lái)牲剃,并返回結(jié)果字符串。

新增的repeat以及strip方法

  1. repeat()方法

repeat(int count)方法可以將原字符串重復(fù)指定次數(shù)雄可,并返回一個(gè)新字符串凿傅。例如:

javaCopy code
String str = "hello";
String repeatedStr = str.repeat(3);
System.out.println(repeatedStr); // "hellohellohello"

在這個(gè)例子中,我們通過(guò)調(diào)用repeat()方法將字符串"hello"重復(fù)了三次数苫,并返回了一個(gè)新字符串"hellohellohello"聪舒。

  1. strip()方法

strip()方法用于去除字符串兩端的空白字符,包括空格虐急、制表符和換行符等箱残,返回一個(gè)新字符串。例如:

javaCopy code
String str = "  hello  \n";
String strippedStr = str.strip();
System.out.println(strippedStr); // "hello"

在這個(gè)例子中戏仓,原字符串為" hello \n"疚宇,包含兩個(gè)前導(dǎo)空格和一個(gè)換行符亡鼠,調(diào)用strip()方法后返回的新字符串為"hello",兩端的空格和換行符都被去掉了敷待。

除了strip()方法间涵,Java 9還新增了stripLeading()stripTrailing()方法,分別用于去除字符串的前導(dǎo)空格和尾部空格榜揖。這些方法對(duì)于處理輸入數(shù)據(jù)和進(jìn)行字符串比較時(shí)非常有用勾哩。

字符串常量池的優(yōu)化

在Java 9之前,字符串常量池是在永久代(PermGen)中實(shí)現(xiàn)的举哟。這意味著在運(yùn)行時(shí)思劳,所有的字符串常量都被存儲(chǔ)在一塊固定的內(nèi)存區(qū)域中。這種實(shí)現(xiàn)方式存在一些問(wèn)題妨猩,比如常量池容易被填滿潜叛,導(dǎo)致OutOfMemoryError異常;并且壶硅,永久代是Java虛擬機(jī)中一個(gè)相對(duì)較小的區(qū)域威兜,因此可能會(huì)導(dǎo)致內(nèi)存不足的問(wèn)題。

在Java 8中庐椒,永久代被移除椒舵,字符串常量池被轉(zhuǎn)移到了堆(Heap)中。這種實(shí)現(xiàn)方式解決了一些問(wèn)題约谈,但仍然存在一些性能和內(nèi)存使用方面的問(wèn)題笔宿。在Java 9中,字符串常量池進(jìn)行了優(yōu)化棱诱,主要包括以下幾個(gè)方面:

  1. 字符串常量池被移到了元空間(Metaspace)中泼橘,這是一個(gè)更大的內(nèi)存區(qū)域,可以動(dòng)態(tài)調(diào)整大小军俊,從而避免了OutOfMemoryError異常侥加。

  2. 在元空間中捧存,字符串常量池使用了一種新的數(shù)據(jù)結(jié)構(gòu)粪躬,稱為“G1特殊化常量池”。這種數(shù)據(jù)結(jié)構(gòu)在性能和內(nèi)存使用方面都有所優(yōu)化昔穴,能夠更快地查找和添加常量镰官。

  3. 對(duì)于使用字符串常量的程序,編譯器現(xiàn)在會(huì)生成更高效的字節(jié)碼吗货,以利用這些優(yōu)化泳唠。例如,編譯器可以使用ldc2指令來(lái)加載常量池中的雙字節(jié)字符串宙搬,而不是使用兩個(gè)ldc指令笨腥。

總之拓哺,Java 9中對(duì)字符串常量池的優(yōu)化使得它更加高效和可靠,能夠更好地滿足大規(guī)模應(yīng)用程序的需要脖母。

性能分析

前面說(shuō)了這么多士鸥,都是關(guān)于JDK9性能上的一些優(yōu)化介紹,但是具體提升了多少呢谆级?下面使用一些直觀的例子來(lái)感受一下到底提升了多少:

字符串拼接

首先來(lái)看看字符串拼接烤礁,一直以來(lái)我們編程中有一個(gè)原則:對(duì)于頻繁拼接字符串的操作,不要直接使用String肥照,而是考慮使用StringBuilder或者StringBuffer脚仔,這是因?yàn)镾tring的不可變特性,導(dǎo)致在拼接過(guò)程中會(huì)產(chǎn)生大量的String對(duì)象從而導(dǎo)致內(nèi)存浪費(fèi):

運(yùn)行下面這段代碼:

long startTime = System.nanoTime();
String s = "";
for (int i = 0; i < 100000; i++) {
    s += "a";
}
long endTime = System.nanoTime();
System.out.println("cost time: " + (endTime - startTime) + "ns");

將這段代碼分別放到JDK1.8和JDK1.9版本對(duì)比一下執(zhí)行時(shí)間舆绎,這里推薦一個(gè)在線的Java編譯運(yùn)行網(wǎng)站鲤脏,支持動(dòng)態(tài)選擇JDK版本:https://www.jdoodle.com/online-java-compiler

image.png

為了稍微準(zhǔn)確一點(diǎn),排除一定的偶然性吕朵,我JDK1.8和JDK1.9各自都運(yùn)行了三遍:

// JDK1.8
cost time: 8043104665ns
cost time: 8029676317ns
cost time: 8084159060ns
//JDK1.9
cost time: 1596969483ns
cost time: 2130420129ns
cost time: 2150930645ns

這里可以看到凑兰,在十萬(wàn)級(jí)別的字符串拼接操作中,JDK1.9的性能提升屬于倍數(shù)級(jí)別的提升了边锁,基本都在四倍左右的提升姑食。

同時(shí)每次執(zhí)行時(shí),頁(yè)面上結(jié)果輸出框的上方也有對(duì)應(yīng)的cpu time和memory數(shù)據(jù):

//JDK1.8
1394148 kilobyte(s)
1394176 kilobyte(s)
1394388 kilobyte(s)
//JDK1.9
660312 kilobyte(s)
660324 kilobyte(s)
660500 kilobyte(s)

可以看到茅坛,這內(nèi)存占用上直接下降了一個(gè)量級(jí)音半,由此可見(jiàn)JDK1.9在字符串的拼接這塊,性能上得到了巨大的提升贡蓖。

字符串替換

long startTime = System.nanoTime();
for (int i =  0; i < 100000; i++) {
    String str = "abcdefg";
    str.replace("c", "x");
}
long endTime = System.nanoTime();
System.out.println("cost time: " + (endTime - startTime) + "ns");

同樣的操作曹鸠,各自執(zhí)行3次看看情況:

//JDK1.8
cost time: 130509721ns
cost time: 226153962ns
cost time: 152726076ns
//JDK1.9
cost time: 53052617ns
cost time: 52959038ns
cost time: 66009582ns
/////////////memory////////
//JDK1.8
97120 kilobyte(s)
97096 kilobyte(s)
96936 kilobyte(s)
//JDK1.9
48488 kilobyte(s)
49688 kilobyte(s)
49732 kilobyte(s)

這里可以看到在效率上,直接下降了一個(gè)量級(jí)斥铺,同時(shí)在哪存上也有接近50%的節(jié)約彻桃。

至于其它一些常規(guī)的方法,比如:查找(indexOf)晾蜘、分割(split)這里JDK1.8和JDK1.9并沒(méi)有多少提升邻眷,甚至可能出現(xiàn)JDK1.9的執(zhí)行效率上反而更低,這并不是說(shuō)9版本中出現(xiàn)了倒退剔交,而是內(nèi)部實(shí)現(xiàn)的算法不一樣肆饶,可能在某些比較特殊的場(chǎng)景下,JDK1.9更有優(yōu)勢(shì)岖常,就像一些排序算法一樣驯镊,雖然時(shí)間復(fù)雜度可以衡量,但是在數(shù)據(jù)量不大的時(shí)候,可能一些復(fù)雜度高的算法反而效果更好板惑。

總體來(lái)說(shuō)橄镜,JDK1.9中String得split方法相比于JDK1.8,一般認(rèn)為有如下改進(jìn):

  1. 使用新的UTF-16字符串匹配器冯乘,代替了JDK 8中使用的正則表達(dá)式引擎蛉鹿。

  2. 改進(jìn)了分隔符的識(shí)別,當(dāng)分隔符為單個(gè)字符時(shí)往湿,使用位運(yùn)算進(jìn)行匹配妖异,避免了使用正則表達(dá)式引擎的開(kāi)銷。

  3. 引入了一種優(yōu)化领追,當(dāng)字符串不包含分隔符時(shí)他膳,不進(jìn)行任何操作直接返回原字符串。這里的好處就是避免一些極端情況下產(chǎn)生大量冗余重復(fù)的字符串對(duì)象

  4. 一些內(nèi)部的實(shí)現(xiàn)細(xì)節(jié)上做了微調(diào)绒窑。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末棕孙,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子些膨,更是在濱河造成了極大的恐慌蟀俊,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件订雾,死亡現(xiàn)場(chǎng)離奇詭異肢预,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)洼哎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門烫映,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人噩峦,你說(shuō)我怎么就攤上這事锭沟。” “怎么了识补?”我有些...
    開(kāi)封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵族淮,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我凭涂,道長(zhǎng)祝辣,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任导盅,我火速辦了婚禮较幌,結(jié)果婚禮上揍瑟,老公的妹妹穿的比我還像新娘白翻。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布滤馍。 她就那樣靜靜地躺著岛琼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪巢株。 梳的紋絲不亂的頭發(fā)上槐瑞,一...
    開(kāi)封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音阁苞,去河邊找鬼困檩。 笑死,一個(gè)胖子當(dāng)著我的面吹牛那槽,可吹牛的內(nèi)容都是我干的悼沿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼骚灸,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼糟趾!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起甚牲,我...
    開(kāi)封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤义郑,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后丈钙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體非驮,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年雏赦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了院尔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡喉誊,死狀恐怖邀摆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情伍茄,我是刑警寧澤栋盹,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站敷矫,受9級(jí)特大地震影響例获,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜曹仗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一榨汤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧怎茫,春花似錦收壕、人聲如沸妓灌。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)虫埂。三九已至,卻和暖如春圃验,著一層夾襖步出監(jiān)牢的瞬間掉伏,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工澳窑, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留斧散,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓摊聋,卻偏偏與公主長(zhǎng)得像颅湘,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子栗精,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 首先String不屬于8種基本數(shù)據(jù)類型闯参,String是一種引用數(shù)據(jù)類型,其默認(rèn)值是null悲立。String類在Jav...
    Yaan9閱讀 215評(píng)論 0 1
  • Java之String 開(kāi)篇 下面這段代碼的輸出: String對(duì)象的內(nèi)部實(shí)現(xiàn) 圖示: 在 Java6 以及之前的...
    Cool_Pomelo閱讀 852評(píng)論 2 13
  • 前提回顧 java.lang.String類用于描述字符串鹿寨,Java程序中所有的字符串字面值都可以使用該類的對(duì)象加...
    洛神灬殤閱讀 200評(píng)論 0 1
  • String 對(duì)象是我們使用最頻繁的一個(gè)對(duì)象類型,但它的性能問(wèn)題卻是最容易被忽略的薪夕。String 對(duì)象作為 Jav...
    han741閱讀 796評(píng)論 0 1
  • 所有字符串用“”進(jìn)行定義脚草,也可以利用“+”實(shí)現(xiàn)字符串的連接 字符串不是基本數(shù)據(jù)類型。 范例:String 類的實(shí)例...
    六藝str閱讀 281評(píng)論 0 0