![](/img/trans.png)
[英]Java - How to use the string.format() to replace any string concatenation within a Sql statement creation?
[英]Is it better practice to use String.format over string Concatenation in Java?
在 Java 中使用String.format
和字符串連接之間有明顯的區別嗎?
我傾向於使用String.format
但偶爾會滑倒並使用連接。 我想知道一個是否比另一個更好。
在我看來, String.format
為您提供了更多“格式化”字符串的能力; 連接意味着您不必擔心不小心放入額外的 %s 或遺漏一個。
String.format
也更短。
哪一個更具可讀性取決於你的頭腦是如何工作的。
我建議最好使用String.format()
。 主要原因是String.format()
可以更輕松地使用從資源文件加載的文本進行本地化,而如果不為每種語言生成具有不同代碼的新可執行文件,則無法對連接進行本地化。
如果您計划將您的應用程序本地化,您還應該養成為格式標記指定參數位置的習慣:
"Hello %1$s the time is %2$t"
然后可以將其本地化並交換名稱和時間標記,而無需重新編譯可執行文件以考慮不同的排序。 使用參數位置,您還可以重復使用相同的參數,而無需將其傳遞給函數兩次:
String.format("Hello %1$s, your name is %1$s and the time is %2$t", name, time)
關於性能:
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
for(int i = 0; i < 1000000; i++){
String s = "Hi " + i + "; Hi to you " + i*2;
}
long end = System.currentTimeMillis();
System.out.println("Concatenation = " + ((end - start)) + " millisecond") ;
start = System.currentTimeMillis();
for(int i = 0; i < 1000000; i++){
String s = String.format("Hi %s; Hi to you %s",i, + i*2);
}
end = System.currentTimeMillis();
System.out.println("Format = " + ((end - start)) + " millisecond");
}
計時結果如下:
因此,連接比 String.format 快得多。
由於有關於性能的討論,我想我會添加一個包含 StringBuilder 的比較。 事實上,它比 concat 更快,當然也比 String.format 選項更快。
為了使這成為一種蘋果與蘋果的比較,我在循環中而不是在循環中實例化了一個新的 StringBuilder(這實際上比僅進行一次實例化要快,很可能是由於在循環末尾重新分配空間的開銷一名建造者)。
String formatString = "Hi %s; Hi to you %s";
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = String.format(formatString, i, +i * 2);
}
long end = System.currentTimeMillis();
log.info("Format = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = "Hi " + i + "; Hi to you " + i * 2;
}
end = System.currentTimeMillis();
log.info("Concatenation = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
StringBuilder bldString = new StringBuilder("Hi ");
bldString.append(i).append("; Hi to you ").append(i * 2);
}
end = System.currentTimeMillis();
log.info("String Builder = " + ((end - start)) + " millisecond");
哪一個更具可讀性取決於您的大腦如何工作。
你就在那里得到了答案。
這是個人口味的問題。
我想,字符串連接稍微快一點,但這應該可以忽略不計。
這是一個以毫秒為單位的多個樣本大小的測試。
public class Time {
public static String sysFile = "/sys/class/camera/rear/rear_flash";
public static String cmdString = "echo %s > " + sysFile;
public static void main(String[] args) {
int i = 1;
for(int run=1; run <= 12; run++){
for(int test =1; test <= 2 ; test++){
System.out.println(
String.format("\nTEST: %s, RUN: %s, Iterations: %s",run,test,i));
test(run, i);
}
System.out.println("\n____________________________");
i = i*3;
}
}
public static void test(int run, int iterations){
long start = System.nanoTime();
for( int i=0;i<iterations; i++){
String s = "echo " + i + " > "+ sysFile;
}
long t = System.nanoTime() - start;
String r = String.format(" %-13s =%10d %s", "Concatenation",t,"nanosecond");
System.out.println(r) ;
start = System.nanoTime();
for( int i=0;i<iterations; i++){
String s = String.format(cmdString, i);
}
t = System.nanoTime() - start;
r = String.format(" %-13s =%10d %s", "Format",t,"nanosecond");
System.out.println(r);
start = System.nanoTime();
for( int i=0;i<iterations; i++){
StringBuilder b = new StringBuilder("echo ");
b.append(i).append(" > ").append(sysFile);
String s = b.toString();
}
t = System.nanoTime() - start;
r = String.format(" %-13s =%10d %s", "StringBuilder",t,"nanosecond");
System.out.println(r);
}
}
TEST: 1, RUN: 1, Iterations: 1
Concatenation = 14911 nanosecond
Format = 45026 nanosecond
StringBuilder = 3509 nanosecond
TEST: 1, RUN: 2, Iterations: 1
Concatenation = 3509 nanosecond
Format = 38594 nanosecond
StringBuilder = 3509 nanosecond
____________________________
TEST: 2, RUN: 1, Iterations: 3
Concatenation = 8479 nanosecond
Format = 94438 nanosecond
StringBuilder = 5263 nanosecond
TEST: 2, RUN: 2, Iterations: 3
Concatenation = 4970 nanosecond
Format = 92976 nanosecond
StringBuilder = 5848 nanosecond
____________________________
TEST: 3, RUN: 1, Iterations: 9
Concatenation = 11403 nanosecond
Format = 287115 nanosecond
StringBuilder = 14326 nanosecond
TEST: 3, RUN: 2, Iterations: 9
Concatenation = 12280 nanosecond
Format = 209051 nanosecond
StringBuilder = 11818 nanosecond
____________________________
TEST: 5, RUN: 1, Iterations: 81
Concatenation = 54383 nanosecond
Format = 1503113 nanosecond
StringBuilder = 40056 nanosecond
TEST: 5, RUN: 2, Iterations: 81
Concatenation = 44149 nanosecond
Format = 1264241 nanosecond
StringBuilder = 34208 nanosecond
____________________________
TEST: 6, RUN: 1, Iterations: 243
Concatenation = 76018 nanosecond
Format = 3210891 nanosecond
StringBuilder = 76603 nanosecond
TEST: 6, RUN: 2, Iterations: 243
Concatenation = 91222 nanosecond
Format = 2716773 nanosecond
StringBuilder = 73972 nanosecond
____________________________
TEST: 8, RUN: 1, Iterations: 2187
Concatenation = 527450 nanosecond
Format = 10291108 nanosecond
StringBuilder = 885027 nanosecond
TEST: 8, RUN: 2, Iterations: 2187
Concatenation = 526865 nanosecond
Format = 6294307 nanosecond
StringBuilder = 591773 nanosecond
____________________________
TEST: 10, RUN: 1, Iterations: 19683
Concatenation = 4592961 nanosecond
Format = 60114307 nanosecond
StringBuilder = 2129387 nanosecond
TEST: 10, RUN: 2, Iterations: 19683
Concatenation = 1850166 nanosecond
Format = 35940524 nanosecond
StringBuilder = 1885544 nanosecond
____________________________
TEST: 12, RUN: 1, Iterations: 177147
Concatenation = 26847286 nanosecond
Format = 126332877 nanosecond
StringBuilder = 17578914 nanosecond
TEST: 12, RUN: 2, Iterations: 177147
Concatenation = 24405056 nanosecond
Format = 129707207 nanosecond
StringBuilder = 12253840 nanosecond
這是與上面相同的測試,修改了在StringBuilder上調用toString()方法。 下面的結果表明 StringBuilder 方法比使用+運算符的字符串連接慢一點。
文件:StringTest.java
class StringTest {
public static void main(String[] args) {
String formatString = "Hi %s; Hi to you %s";
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = String.format(formatString, i, +i * 2);
}
long end = System.currentTimeMillis();
System.out.println("Format = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = "Hi " + i + "; Hi to you " + i * 2;
}
end = System.currentTimeMillis();
System.out.println("Concatenation = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
StringBuilder bldString = new StringBuilder("Hi ");
bldString.append(i).append("Hi to you ").append(i * 2).toString();
}
end = System.currentTimeMillis();
System.out.println("String Builder = " + ((end - start)) + " millisecond");
}
}
Shell 命令:(編譯並運行 StringTest 5 次)
> javac StringTest.java
> sh -c "for i in \$(seq 1 5); do echo \"Run \${i}\"; java StringTest; done"
結果 :
Run 1
Format = 1290 millisecond
Concatenation = 115 millisecond
String Builder = 130 millisecond
Run 2
Format = 1265 millisecond
Concatenation = 114 millisecond
String Builder = 126 millisecond
Run 3
Format = 1303 millisecond
Concatenation = 114 millisecond
String Builder = 127 millisecond
Run 4
Format = 1297 millisecond
Concatenation = 114 millisecond
String Builder = 127 millisecond
Run 5
Format = 1270 millisecond
Concatenation = 114 millisecond
String Builder = 126 millisecond
通常,字符串連接應該優先於String.format
。 后者有兩個主要缺點:
第 1 點,我的意思是不可能理解String.format()
調用在單個順序傳遞中正在做什么。 一個人被迫在格式字符串和參數之間來回移動,同時計算參數的位置。 對於短連接,這不是什么大問題。 然而,在這些情況下,字符串連接不那么冗長。
第 2 點,我的意思是構建過程的重要部分是在格式字符串中編碼的(使用 DSL)。 使用字符串來表示代碼有很多缺點。 它本質上不是類型安全的,並且使語法突出顯示、代碼分析、優化等復雜化。
當然,在使用 Java 語言外部的工具或框架時,新的因素可能會發揮作用。
String.format()
不僅僅是連接字符串。 例如,您可以使用String.format()
在特定語言環境中顯示數字。
但是,如果您不關心本地化,則沒有功能差異。 也許一個比另一個快,但在大多數情況下,它可以忽略不計。
錯誤的測試重復了很多次你應該使用 {} no %s。
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = "Hi " + i + "; Hi to you " + i * 2;
}
long end = System.currentTimeMillis();
System.out.println("Concatenation = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = String.format("Hi %s; Hi to you %s", i, +i * 2);
}
end = System.currentTimeMillis();
System.out.println("Wrong use of the message format = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = String.format("Hi {0}; Hi to you {1}", i, +i * 2);
}
end = System.currentTimeMillis();
System.out.println("Good use of the message format = " + ((end - start)) + " millisecond");
}
Concatenation = 88 millisecond
Wrong use of the message format = 1075 millisecond
Good use of the message format = 376 millisecond
可能會有明顯的差異。
String.format
非常復雜,並且在下面使用了正則表達式,所以不要養成在任何地方使用它的習慣,而只在需要的地方使用它。
StringBuilder
會快一個數量級(正如這里有人已經指出的那樣)。
我沒有做過任何具體的基准測試,但我認為串聯可能會更快。 String.format() 創建了一個新的 Formatter,它反過來又創建了一個新的 StringBuilder(大小只有 16 個字符)。 這是相當大的開銷,特別是如果您正在格式化更長的字符串並且 StringBuilder 必須不斷調整大小。
但是,連接的用處不大,而且更難閱讀。 與往常一樣,值得對您的代碼進行基准測試以查看哪個更好。 在您的資源包、區域設置等加載到內存中並且代碼經過 JIT 處理后,服務器應用程序中的差異可能可以忽略不計。
也許作為最佳實踐,使用適當大小的 StringBuilder (Appendable) 和 Locale 創建您自己的 Formatter 是一個好主意,如果您有很多格式要做,請使用它。
我認為我們可以使用MessageFormat.format
因為它應該在可讀性和性能方面都很好。
我使用了與Icaro在上述答案中使用的程序相同的程序,並通過附加代碼對其進行了增強,以使用MessageFormat
來解釋性能數字。
public static void main(String[] args) {
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = "Hi " + i + "; Hi to you " + i * 2;
}
long end = System.currentTimeMillis();
System.out.println("Concatenation = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = String.format("Hi %s; Hi to you %s", i, +i * 2);
}
end = System.currentTimeMillis();
System.out.println("Format = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = MessageFormat.format("Hi %s; Hi to you %s", i, +i * 2);
}
end = System.currentTimeMillis();
System.out.println("MessageFormat = " + ((end - start)) + " millisecond");
}
串聯 = 69 毫秒
格式 = 1435 毫秒
消息格式 = 200 毫秒
更新:
根據 SonarLint 報告,應正確使用 Printf 樣式的格式字符串 (squid:S3457)
因為printf-style
格式字符串在運行時解釋,而不是由編譯器驗證,所以它們可能包含導致創建錯誤字符串的錯誤。 當調用java.util.Formatter
、 java.lang.String
、 java.io.PrintStream
、 MessageFormat
和java.io.PrintWriter
的 format(...) 方法時,此規則靜態驗證printf-style
格式字符串與其參數的相關性java.io.PrintWriter
類和java.io.PrintStream
或java.io.PrintWriter
類的printf(...)
方法。
我用大括號替換了 printf 樣式,得到了一些有趣的結果,如下所示。
串聯 = 69 毫秒
格式 = 1107 毫秒
格式:花括號 = 416 毫秒
消息格式 = 215 毫秒
消息格式:大括號 = 2517 毫秒
我的結論:
正如我在上面強調的,使用帶有花括號的 String.format 應該是一個不錯的選擇,以獲得良好的可讀性和性能的好處。
習慣 String.Format 需要一點時間,但在大多數情況下是值得的。 在 NRA 的世界中(永遠不要重復任何事情),將標記化的消息(日志記錄或用戶)保存在常量庫中(我更喜歡靜態類)並在必要時使用 String.Format 調用它們非常有用,無論您是否是否本地化。 嘗試使用具有串聯方法的此類庫更難通過任何需要串聯的方法來閱讀、排除故障、校對和管理。 更換是一種選擇,但我懷疑它的性能。 經過多年的使用,我對 String.Format 的最大問題是,當我將它傳遞給另一個函數(如 Msg)時,調用的長度很長很不方便,但是使用自定義函數作為別名很容易解決.
您無法通過上述程序比較 String Concatenation 和 String.Format。
您也可以嘗試在代碼塊中交換使用 String.Format 和 Concatenation 的位置,如下所示
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
for( int i=0;i<1000000; i++){
String s = String.format( "Hi %s; Hi to you %s",i, + i*2);
}
long end = System.currentTimeMillis();
System.out.println("Format = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for( int i=0;i<1000000; i++){
String s = "Hi " + i + "; Hi to you " + i*2;
}
end = System.currentTimeMillis();
System.out.println("Concatenation = " + ((end - start)) + " millisecond") ;
}
您會驚訝地發現 Format 在這里運行得更快。 這是因為創建的初始對象可能不會被釋放,內存分配可能會出現問題,從而導致性能問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.