日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Java 字符串拼接的各种玩法

發(fā)布時間:2025/3/12 java 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java 字符串拼接的各种玩法 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

java中字符串拼接各種方式與執(zhí)行效率

由于String對象是不可變對象,因此在需要對字符串進(jìn)行修改操作時(如字符串連接和替換),String對象總是會生成新的對象,所以其性能相對較差。
 
String常量的累加操作:對于靜態(tài)字符串的連接操作,Java在編譯時會進(jìn)行徹底的優(yōu)化,將多個連接操作的字符串在編譯時合成一個單獨的長字符串。

String變量的累加操作:底層使用了StringBuilder的功能。
  
StringBuffer和StringBuilder的擴容分析

當(dāng)字符串緩沖區(qū)容量不足時,原有容量將會加倍,以新的容量來申請內(nèi)存空間,建立新的char數(shù)組,然后將原數(shù)組中的內(nèi)容復(fù)制到這個新的數(shù)組當(dāng)中。因此,對于大對象的擴容會涉及大量的內(nèi)存復(fù)制操作。所以,如果能夠預(yù)先評估StringBuilder或StringBuffer的大小,將能夠有效的節(jié)省這些操作,從而提高系統(tǒng)的性能。

JAVA的字符串拼接與性能

在JAVA中拼接兩個字符串的最簡便的方式就是使用操作符”+”了。如果你用”+”來連接固定長度的字符串,可能性能上會稍受影響,但是如果你是在循環(huán)中來”+”多個串的話,性能將指數(shù)倍的下降。假設(shè)有一個字符串,我們將對這個字符串做大量循環(huán)拼接操作,使用”+”的話將得到最低的性能。但是究竟這個性能有多差?
  
   +, Join,StringBuffer,StringBuilder或String.concat()
   
  1 . String 是final對象,不會被修改,每次使用 + 進(jìn)行拼接都會創(chuàng)建新的對象,而不是改變原來的對象,也屬于線程安全的;
  
  2 . StringBuffer可變字符串,主要用于字符串的拼接,屬于線程安全的;(StringBuffer的append操作用了synchronized)
  
  3 . StringBuilder可變字符串,主要用于字符串的拼接,屬于線程不安全的;
  
1 循環(huán)操作:

import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.StringUtils; public class TestString {public static void main(String[] args) {//plus拼接字符串方式String s = "";long ts = System.currentTimeMillis();for (int i = 0; i < 10000; i++) {s = s + String.valueOf(i);}long te = System.currentTimeMillis();System.out.println("Plus cost {"+( te - ts) +"} ms");//concat拼接字符串方式String s2 = "";long ts2 = System.currentTimeMillis();for (int i = 0; i < 10000; i++) {s2 = s2.concat(String.valueOf(i));}long te2 = System.currentTimeMillis();System.out.println("concat cost {"+(te2 - ts2)+"} ms");//StringUtils.join拼接字符串方式List<String> list = new ArrayList<String>();long ts3 = System.currentTimeMillis();for (int i = 0; i < 10000; i++) {list.add(String.valueOf(i));}StringUtils.join(list, "");long te3 = System.currentTimeMillis();System.out.println("StringUtils.join cost {"+(te3 - ts3)+"} ms");//StringBuffer拼接字符串方式StringBuffer sb = new StringBuffer();long ts4 = System.currentTimeMillis();for (int i = 0; i < 100000; i++) {sb.append(String.valueOf(i));}sb.toString();long te4 = System.currentTimeMillis();System.out.println("StringBuffer cost {"+(te4 - ts4)+"} ms");//StringBuilder拼接字符串方式StringBuilder sb5 = new StringBuilder();long ts5 = System.currentTimeMillis();for (int i = 0; i < 100000; i++) {sb5.append(String.valueOf(i));}sb5.toString();long te5 = System.currentTimeMillis();System.out.println("StringBuilder cost {"+(te5 - ts5)+"} ms");} }

特別注意
  StringBuilder和StringBuffer循環(huán)的次數(shù)是其它的10倍,如果是一樣,那么可能返回0,可見StringBuilder和StringBuffer的速度之快。
  
性能分析
  1 . StringBuilder性能比StringBuffer要好點。在1千萬的循環(huán)下, StringBuilder大約在500-600毫秒,而StringBuffer大約在700-800毫秒;
  
  2 . StringUtils.join方式性能也是可以的,在循環(huán)不是很大的情況下,該種方式也是可以考慮的;
  
  3 . 另外2種方式,在循環(huán)的情況下,盡量不要使用。
  
因此,即時在做最簡單的拼接時,如果我們不想創(chuàng)建StringBuffer或StringBuilder實例時,我們也應(yīng)該使用concat。但是對于大量的字符串拼接操作,就不應(yīng)該使用concat,因為concat會降低你程序的性能,消耗你的cpu。因此,在不考慮線程安全和同步的情況下,為了獲得最高的性能,我們應(yīng)盡量使用StringBuilder。

總結(jié)

1.用+的方式效率最差,concat由于是內(nèi)部機制實現(xiàn),比+的方式好了不少。
  
  2.Join和StringBuffer,相差不大,Join方式要快些,可見這種JavaScript中快速拼接字符串的方式在Java中也非常適用。
  
  3.StringBuilder 的速度最快,但其有線程安全的問題,而且只有JDK5及以上的版本支持。
  
  4.String對象串聯(lián)的效率最慢,單線程下字符串的串聯(lián)用StringBuilder,多線程下字符串的串聯(lián)用StrngBuffer。
  
  5.在編譯階段就能夠確定的字符串常量,完全沒有必要創(chuàng)建String或StringBuffer對象。直接使用字符串常量的"+"連接操作效率最高(如:String str = “a” + “b” + “c”;)。

字符串拼接玩法分步深入探析

“+”號操作符

從實用方面來說,“+”號操作符必須是字符串拼接最常用的一種了,沒有之一。

String chenmo = "沉默"; String wanger = "王二"; System.out.println(chenmo + wanger);

我們把這段代碼使用 JAD 反編譯一下。

String chenmo = "u6C89u9ED8"; // 沉默 String wanger = "u738Bu4E8C"; // 王二 System.out.println((new StringBuilder(String.valueOf(chenmo))).append(wanger).toString());

程序在編譯的時候把“+”號操作符替換成了 StringBuilder 的 append 方法。也就是說,“+”號操作符在拼接字符串的時候只是一種形式主義,讓開發(fā)者使用起來比較簡便,代碼看起來比較簡潔,讀起來比較順暢。

StringBuilder

除去“+”號操作符,StringBuilder 的 append 方法就是第二個常用的字符串拼接姿勢了。

先來看一下 StringBuilder 類的 append 方法的源碼:

public StringBuilder append(String str) { super.append(str); return this; }

這 3 行代碼沒啥可看的,可看的是父類

AbstractStringBuilder 的 append 方法: public AbstractStringBuilder append(String str) { if (str == null) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0, len, value, count); count += len; return this; }

1 . 判斷拼接的字符串是不是 null,如果是,當(dāng)做字符串“null”來處理。appendNull 方法的源碼如下:

private AbstractStringBuilder appendNull() { int c = count; ensureCapacityInternal(c + 4); final char[] value = this.value; value[c++] = 'n'; value[c++] = 'u'; value[c++] = 'l'; value[c++] = 'l'; count = c; return this; }

2 . 拼接后的字符數(shù)組長度是否超過當(dāng)前值,如果超過,進(jìn)行擴容并復(fù)制。ensureCapacityInternal 方法的源碼如下:

private void ensureCapacityInternal(int minimumCapacity) { // overflow-conscious code if (minimumCapacity - value.length > 0) { value = Arrays.copyOf(value, newCapacity(minimumCapacity)); } }

3 . 將拼接的字符串 str 復(fù)制到目標(biāo)數(shù)組 value 中。

str.getChars(0, len, value, count)

StringBuffer

先有 StringBuffer 后有 StringBuilder,兩者就像是孿生雙胞胎,該有的都有, StringBuffer 是線程安全的。

public synchronized StringBuffer append(String str) { toStringCache = null; super.append(str); return this; }

StringBuffer 類的 append 方法比 StringBuilder 多了一個關(guān)鍵字 synchronized,可暫時忽略 toStringCache = null。

synchronized 是 Java 中的一個非常容易臉熟的關(guān)鍵字,是一種同步鎖。它修飾的方法被稱為同步方法,是線程安全的。

String 類的 concat 方法

String 類的 concat 方法就好像 StringBuilder 類的 append。

String chenmo = "沉默"; String wanger = "王二"; System.out.println(chenmo.concat(wanger));

在這個時候,我突然產(chǎn)生了一個奇妙的想法。假如有這樣兩行代碼:

chenmo += wanger chenmo = chenmo.concat(wanger)

它們之間究竟有多大的差別呢?
我們已經(jīng)了解到,chenmo += wanger 實際上相當(dāng)于 (new StringBuilder(String.valueOf(chenmo))).append(wanger).toString()。

要探究“+”號操作符和 concat 之間的差別,實際上要看 append 方法和 concat 方法之間的差別。

append 方法的源碼分析過了。我們來看一下 concat 方法的源碼。

public String concat(String str) { int otherLen = str.length(); if (otherLen == 0) { return this; } int len = value.length; char buf[] = Arrays.copyOf(value, len + otherLen); str.getChars(buf, len); return new String(buf, true); }

1 . 如果拼接的字符串的長度為 0,那么返回拼接前的字符串。

if (otherLen == 0) { return this; }

2 . 將原字符串的字符數(shù)組 value 復(fù)制到變量 buf 數(shù)組中。

char buf[] = Arrays.copyOf(value, len + otherLen);

3 . 把拼接的字符串 str 復(fù)制到字符數(shù)組 buf 中,并返回新的字符串對象。

str.getChars(buf, len); return new String(buf, true);

通過分析我們大致可以得出以下結(jié)論

如果拼接的字符串是 null,concat 時候就會拋出 NullPointerException,“+”號操作符會當(dāng)做是“null”字符串來處理。如果拼接的字符串是一個空字符串(""),那么 concat 的效率要更高一點。畢竟不需要 new StringBuilder 對象。如果拼接的字符串非常多,concat 的效率就會下降,因為創(chuàng)建的字符串對象越多,開銷就越大。

注意
在JSP中, EL 表達(dá)式中是不允許使用“+”操作符來拼接字符串的,這時候就只能用 concat 了。

${chenmo.concat('-').concat(wanger)}

String 類的 join 方法

JDK 1.8 提供了一種新的字符串拼接姿勢:String 類增加了一個靜態(tài)方法 join。

String chenmo = "快樂"; String wanger = "王二"; String cmower = String.join("", chenmo, wanger); System.out.println(cmower);

第一個參數(shù)為字符串連接符,比如說:

String message = String.join("-", "王二", "今天", "悶悶不樂的");

輸出結(jié)果為:王二-今天-悶悶不樂的

我們來看一下 join 方法的源碼:

public static String join(CharSequence delimiter, CharSequence... elements) { Objects.requireNonNull(delimiter); Objects.requireNonNull(elements);StringJoiner joiner = new StringJoiner(delimiter); for (CharSequence cs: elements) { joiner.add(cs); } return joiner.toString(); }

發(fā)現(xiàn)了一個新類 StringJoiner。StringJoiner 是 java.util 包中的一個類,用于構(gòu)造一個由分隔符重新連接的字符序列。

StringUtils.join

實戰(zhàn)項目當(dāng)中,我們處理字符串的時候,經(jīng)常會用到這個類——org.apache.commons.lang3.StringUtils,該類的 join 方法是字符串拼接的一種新姿勢。

String chenmo = "快樂"; String wanger = "王二"; StringUtils.join(chenmo, wanger);

該方法更善于拼接數(shù)組中的字符串,并且不用擔(dān)心

NullPointerException。 StringUtils.join(null) = null StringUtils.join([]) = "" StringUtils.join([null]) = "" StringUtils.join(["a", "b", "c"]) = "abc" StringUtils.join([null, "", "a"]) = "a"

通過查看源碼我們可以發(fā)現(xiàn),其內(nèi)部使用的仍然是 StringBuilder。

public static String join(final Object[] array, String separator, final int startIndex, final int endIndex) { if (array == null) { return null; } if (separator == null) { separator = EMPTY; } final StringBuilder buf = new StringBuilder(noOfItems * 16); for (int i = startIndex; i if (i > startIndex) { buf.append(separator); } if (array[i] != null) { buf.append(array[i]); } } return buf.toString(); }

我們看著這里可能會感到驚訝,字符串拼接足足有 6 種姿勢啊,我要好好試一下。

為什么不建議在 for 循環(huán)中使用”+”號操作符進(jìn)行字符串拼接呢?

根據(jù)兩段代碼進(jìn)行分析。

第一段,for 循環(huán)中使用”+”號操作符。

String result = ""; for (int i = 0; i result += "八八八"; }

第二段,for 循環(huán)中使用 append。

StringBuilder sb = new StringBuilder(); for (int i = 0; i sb.append("八八八"); }

這兩段代碼的耗時時間

第一段代碼執(zhí)行完的時間為 6312 毫秒第二段代碼執(zhí)行完的時間為 2毫秒

差距為什么這么大呢?

第一段的 for 循環(huán)中創(chuàng)建了大量的 StringBuilder 對象,而第二段代碼至始至終只有一個 StringBuilder 對象。

總結(jié)

以上是生活随笔為你收集整理的Java 字符串拼接的各种玩法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。