1. 程式人生 > >Java中對於+和append拼接字串效率的誤解

Java中對於+和append拼接字串效率的誤解

引言  

  對於初學Java的人來說,在學習String的時候,肯定有無數個人和我們講過,”儘量不要使用+拼接字串,效率不好,應該使用append,你自己迴圈拼接個十萬次自己瞧瞧就知道了“,然後像下面那樣給我們演示了一下,用+和用StringBuilder的append拼接個十萬次,輸出一下各自消耗的時間,差距非常大,讓我們立刻深信不疑,+拼接就是個垃圾,除了平時方便測試程式碼,否則都不要去用了,並且當有初學者使用+拼接時,也會毫不猶豫地去高談闊論地教育一番。

分析

public class Test {
    public static void main(String[] args){
        String str = ""
; long start = System.currentTimeMillis(); for(int i=0; i<100000; i++) str += "a"; long end = System.currentTimeMillis(); System.out.println(end - start); StringBuilder sb = new StringBuilder(); start = System.currentTimeMillis(); for
(int i=0; i<100000; i++) sb.append("a"); str = sb.toString(); end = System.currentTimeMillis(); System.out.println(end - start); } } /* console: D:\>javac Test.java D:\>java Test 4655 0 */

可以看到確實是差距巨大,並且我們也看過不少下面反編譯的位元組碼指令:

這裡寫圖片描述

上面的方框是+迴圈拼接,下面的方框是append迴圈拼接。
可以看到迴圈中+拼接會建立一個StringBuilder,除此之外和直接使用StringBuilder沒有兩樣,但問題是每次迴圈都會new一個StringBuilder,也就是說效率就差在這裡了。
(在剛學Java7的時候,我從別人那裡得到一個很搞笑的解釋,”+拼接底層實際上是使用了StringBuilder,所以效率很低,因此我們應該在任何時候都使用StringBuffer來拼接字串“

,噗,真不知道這種混亂的邏輯是怎麼產生的,StringBuffer是執行緒安全的怎麼可能比StringBuilder效率高,233333)

規則

對於此處的str = str + “a”,編譯器會處理為new StringBuilder().append(str).append(“a”),不管一次性+幾個字串,只要+拼接全部在一條語句中,就只會new一次,迴圈中+拼接被斷成了十萬條語句,那自然就會new十萬次,證據如下:

public class Test {
    public static void main(String[] args){
        String a = "a";
        String b = "a";
        String c = "a";
        String d = "a";
        String e = a + b + c;
        e += d;
    }
}

反編譯後:
這裡寫圖片描述
可以看到,對於有String型別變數參與的情況(String變數+”字面量” 或者 String變數+String變數),只要+拼接沒有斷,那就會一直呼叫append,一旦另起一條語句,馬上就會new一個StringBuilder.
所以最開始那樣拼接十萬次的測試根本沒有意義,根本不公平,真要測試也應該像下面這樣:

public class Test {
    public static void main(String[] args){
        String str = "";
        long start = System.currentTimeMillis();
        for(int i=0; i<100000; i++)
            str += "a";
        long end = System.currentTimeMillis();
        System.out.println(end - start);

        StringBuilder sb = new StringBuilder();
        start = System.currentTimeMillis();
        for(int i=0; i<100000; i++)
            sb = new StringBuilder().append(sb).append("a");//為了保證公平,這裡也要new
        str = sb.toString();
        end = System.currentTimeMillis();   
        System.out.println(end - start);
    }
}
/*
D:\>javac Test.java

D:\>java Test
4656
3116
*/

這樣看來,差距也沒什麼,實際上如果數量再大些,+效率比直接StringBuilder可能更好。

或者這樣測試(此時已經不是String型別變數而是字面量了):

public class Test {
    public static void main(String[] args){
        long start = System.currentTimeMillis();
        String str = "a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a"+"a";
        long end = System.currentTimeMillis();  
        System.out.println(end - start);

        StringBuilder sb = new StringBuilder();
        start = System.currentTimeMillis();
        //append是可以用迴圈的,這裡故意這麼搞     

        sb.append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a").append("a");
        str = sb.toString();
        end = System.currentTimeMillis();   
        System.out.println(end - start);
    }
}
/*
D:\>javac Test.java

D:\>java Test
0
0
*/

數量太少,不夠用。
這裡寫圖片描述
(把StringBuilder的測試給省了,太長了,截不下)
如果檢視位元組碼指令的話,會發現這200個”a”直接就被合成了一個完整的字串(jdk1.8下的結果,我不知道jdk1.7是不是這樣),沒有使用StringBuilder,這可能和字串的常量池有關,但是從結果來看,字串字面量的拼接是不會使用new的

公司的專案不知出於什麼原因不讓用ORM框架,非要手動拼接SQL語句,對於是否使用+拼接大量SQL還和同事爭論了一番,對於那些靜態固定的SQL,用+來換行增強可讀性完全沒有問題;但是如果要動態改變SQL條件的話,還是應該使用StringBuilder的append,因為動態SQL肯定會根據不同的條件拼接不同的String變數,那麼SQL語句中途肯定會斷開,用+的話每斷開一次都會new一次。



String sql1 = "SELECT COL1, COL2, COL3, COL4 "
         + "FROM TABLE1 LEFT JOIN TABLE2 "
         + "ON TABLE1.ID = TABLE2.ID WHERE "
         + "COL5 = :col5 AND COL6 = :col6";

StringBuilder sb = new StringBuilder();
sb.append("SELECT COL1, COL2, COL3, COL4 FROM TABLE1 ")
  .append("WHERE 1");
if(var1 != null)
    sb.append(" AND COL5 = ").append(var1);
if(var2 != null)
    sb.append(" AND COL6 = ").append(var2);
String sql2 = sb.toString();

其實編譯器還可能對+進行一些優化,比如String a = b + c + d 可能被處理成a = new StringBuilder(b).append(c).append(d)

總結

  Java中+拼接字串並不是人們口中所說的那樣臭名昭著,人們口中所說的效率低下指的是在迴圈中的+拼接,只是不知道從哪天開始完全變了味。如果你能保證在一條語句中把字串或者字串變數全部拼接完而不斷開,那+拼接根本沒有任何缺點,編譯器還會有一些優化,如果拼接的全是字串字面量,那效果更好,它們直接就變成了一個完整的字串。並且+書寫簡潔方便,可讀性強。直接用StringBuilder的append也沒有問題,只是不要append()和+混合使用。

相關推薦

Java對於+append拼接字串效率誤解

引言     對於初學Java的人來說,在學習String的時候,肯定有無數個人和我們講過,”儘量不要使用+拼接字串,效率不好,應該使用append,你自己迴圈拼接個十萬次自己瞧瞧就知道了“,然後像下面那樣給我們演示了一下,用+和用StringBuilder的

javamap表單字串相互轉換

一、需求 serialnum=123456&data=357c0a04f&enable=true&key=b5b806d0dc9 帶有&分割的字串,轉化為map的key、value型別儲存,反之也行。 二、程式碼 import java.util.

java對於字串 XX.toStringString.valueOf()以及強制型別轉換的區別

今天在寫程式碼的時候遇到很多需要型別轉換的東西 ,特別是需要轉換到String型別。好幾次都報錯了,特此記錄一下這三者之間的區別 toString方法 先看一下API對toString方法的描述 返回該物件的字串表示。通常,toString 方法會返回一個“以文

java對於Date時間的各種用法方法總結

在java中經常會用到和時間日期有關的各種操作,今天就對於Date的幾中常用的用法做一個總結: -Date類 : 1.表示日期的類 2.提供了很多的操作日期的方法,但是很多方法被java標記為過時 public class TestDateAPIClass { @Test

javaString new String還有物件的String字串在記憶體的儲存

一直以來,所有人都說,java中的String型別是不可變的,可是為什麼不可變確很少有人說的透徹,String和new String的區別,物件中的String和直接定義一個String是否有區別,一直都是一知半解。看了很多文件都是各種猜測,沒有具體程式碼來

java對於大量資料採用批量處理來提高效率

  設計的話, 是在dao層寫批量新增的方法,以及實現類dao的實現類, 在service呼叫這個dao就可以了!   不過最終走的還是單個只不過是集合的遍歷, 所以不用再mapper.xml裡面配置方法。 IReconBankOrderCpsBatchDao裡面的方法:

Java 8不再需要StringBuilder拼接字串

在Java開發者中,字串的拼接佔用資源高往往是熱議的話題. 讓我們深入討論一下為什麼會佔用高資源。 在Java中,字串物件是不可變的,意思是它一旦建立,你就無法再改變它。所以在我們拼接字串的時候,建立了一個新的字串,舊的被垃圾回收器所標記。 如果我們處理上百萬

java對於輸入輸出的一個簡單概念瞭解

在I/O中有著“流”的抽象概念,它代表任何有能力產出資料的資料來源物件或者是有能力接收資料的接收端物件。Java類庫中的I/O類分成輸入和輸出兩部分。輸入:通過繼承,任何自Inputstream或Reader派生而來的類都含有名為read()的基本方法,用於讀取單個位元組或者

jsjava日期日期字串的相互轉換使用

摘要:在工作中只要牽扯到日期,很大可能都會牽扯到日期的計算格式的轉換等用法,這篇文章就是要探討一下平常在 js和java中對於日期的使用。 js中日期的使用 js中日期的計算 和 比較 js中兩個日期字串的計算 ###########

c++拼接字串效率比較(+=、append、stringstream、sprintf)

最近寫的程式用到大量拼接字串,為了提高拼接效率,比較了一下+=、append、stringstream、sprintf四種拼接字串的方法。 測試方法 比較方法是寫了4個函式,分別用+=、append、stringstream、sprintf的方式來拼接字串

ajax請求得到後臺資料,前臺頁面,使用table模板然後clone,顯示錶格,,不用append拼接字串HTML表格標籤”

<%@page import="java.util.ArrayList"%> <%@page import="java.util.List"%> <%@ page language="java" contentType="text/html; charset=utf-8"   

JavaIntegerint比較大小出現的錯誤

最好 裏的 pan 轉換 als 範圍 urn 返回 錯誤 Java在某一處維護著一個常量池,(我記得)在小於128的範圍內,直接用 1 Integer i = 100; 2 int j = 100; 3 return i == j;//true 這裏返回的是true.

javaArrayListLinkedList區別

插入 list 新的 查找 arr tro 基於 列表 時間復雜度 ArrayList和LinkedList最主要的區別是基於不同數據結構 ArrayList是基於動態數組的數據結構,LinkedList基於鏈表的數據結構,針對這點,從時間復雜度和空間復雜度來看主要區別:

java棧的區別

mem 線程 所有 生成 werror 空間 調用 訪問 指向 01,各司其職;         棧內存用來存儲局部變量和方法的調用,         而堆內存用來存儲java中的對象,無論是成員變量,局部變量,還是類變量         他們指向的對象都存儲在堆內存中。

JavaPreparedStatementStatement的用法區別

aik txt 實例 什麽 一點 所有 一個 drop passwd Java中PreparedStatement和Statement的用法區別 (2012-08-01 11:06:44) 轉載▼ 標簽: 雜談 1、 PreparedStatem

javaComparator Comparable的區別

true public arr ins ride err instance ural code 1、Comparable的代碼如下: public interface Comparable<T> { public int compareTo(T o);

JAVA日期時間的格式化選項

println 對象 bsp lec pub cti class 日子 月份 一、使用printf方法 1 import java.util.Date; 2 import java.util.Scanner; 3 4 5 public class Test

【轉載】JavaComparableComparator比較

import 比較器 todo itl 復制代碼 ack div array open 【本文轉自】http://www.cnblogs.com/skywang12345/p/3324788.html Comparable 簡介 Comparable 是排序接口。 若一

Javaarraylistlinkedlist源代碼分析與性能比較

rom fin java 獲取 color () serializa padding previous Java中arraylist和linkedlist源代碼分析與性能比較 1,簡單介紹 在java開發中比較經常使用的數據結構是arra

java thissuper的差別

屏蔽 code args mod -h ans 使用 -a oid this表示當前調用方法的對象的引用: (誰調用這種方法,誰就是這個對象,這個this就是它的引用) 比方:<pre name="code" class="j