![](/img/trans.png)
[英]How can we optimize the following code for If Else Optimization in Java?
[英]Java code optimization, will it optimize this
對於較新的編譯器,我發現自己試圖編寫更容易閱讀的代碼,但如果我希望在引擎蓋下完成的優化實際上沒有完成,那么可能需要更多的內存。 以此代碼為例,非常簡單
while (scanner.hasNextLine() && !result)
{
String line = scanner.nextLine();
result = line.indexOf(searchString) >= 0;
}
假設(使用Eclipse Juno,Java 7)這將產生與之相同的字節代碼是否公平
while (scanner.hasNextLine() && !result)
{
result = scanner.nextLine().indexOf(searchString) >= 0;
}
前者雖然2行代碼減少了第二行的長度並使其更容易在眼睛上。 恕我直言但它是否也會導致創建一個無關的String對象? 我希望不是 ...
您無法轉義正在創建的String
。 將它分配給局部變量這一事實在這里是無關緊要的,實際上在字節碼級別,這個事實甚至不會被注意到:在該級別,無論是否有顯式變量,對結果的引用必須放在堆棧中為了傳遞到鏈中的下一個方法調用。
你的想法,不必要的String
創建實例可以從一種本能,從另一種語言,如C來干:分配String s = ...
只復制的參考唯一的字符串實例。 這是因為所有Java對象都駐留在堆上,因此您始終需要顯式復制對象以實際涉及另一個實例。 例如,如果您編寫了String line = new String(scanner.nextLine())
,那確實會創建一個不必要的String
實例。
總而言之,任何版本的代碼都不涉及優化,因此請僅根據樣式首選項進行選擇。
一些一般原則:
在您的特定情況下:變量聲明在optimitation方面不會改變任何東西,因為在這兩種情況下,一個字符串由nextLine()
並放置在堆棧上,將其分配給一個變量(在字節碼中消失,除非它是一個實例變量,因為它的用處僅適用於你的眼睛)不會改變任何東西。
為什么不問Java Class File Disassembler - 每個JDK中包含的javap
程序?
擁有以下源代碼:
public class Foo {
static void m1(Scanner scanner, String searchString, boolean result) {
while (scanner.hasNextLine() && !result) {
String line = scanner.nextLine();
result = line.indexOf(searchString) >= 0;
}
}
static void m2(Scanner scanner, String searchString, boolean result) {
while (scanner.hasNextLine() && !result) {
result = scanner.nextLine().indexOf(searchString) >= 0;
}
}
}
運行反匯編程序時:
javap -c Foo.class
您將獲得以下字節碼:
static void m1(java.util.Scanner, java.lang.String, boolean);
Code:
0: goto 22
3: aload_0
4: invokevirtual #33 // Method java/util/Scanner.nextLine:()Ljava/lang/String;
7: astore_3
8: aload_3
9: aload_1
10: invokevirtual #39 // Method java/lang/String.indexOf:(Ljava/lang/String;)I
13: iflt 20
16: iconst_1
17: goto 21
20: iconst_0
21: istore_2
22: aload_0
23: invokevirtual #45 // Method java/util/Scanner.hasNextLine:()Z
26: ifeq 33
29: iload_2
30: ifeq 3
33: return
static void m2(java.util.Scanner, java.lang.String, boolean);
Code:
0: goto 20
3: aload_0
4: invokevirtual #33 // Method java/util/Scanner.nextLine:()Ljava/lang/String;
7: aload_1
8: invokevirtual #39 // Method java/lang/String.indexOf:(Ljava/lang/String;)I
11: iflt 18
14: iconst_1
15: goto 19
18: iconst_0
19: istore_2
20: aload_0
21: invokevirtual #45 // Method java/util/Scanner.hasNextLine:()Z
24: ifeq 31
27: iload_2
28: ifeq 3
31: return
如果你比較兩種方法的字節碼,你會發現唯一的區別是m1
包含這兩個額外的指令:
7: astore_3
8: aload_3
這只是將對堆棧頂部對象的引用存儲到局部變量中,而不是其他任何東西。
編輯:
反匯編程序還可以顯示方法的局部變量數:
javap -l Foo.class
哪個輸出:
static void m1(java.util.Scanner, java.lang.String, boolean);
LocalVariableTable:
Start Length Slot Name Signature
0 34 0 scanner Ljava/util/Scanner;
0 34 1 searchString Ljava/lang/String;
0 34 2 result Z
8 14 3 line Ljava/lang/String;
static void m2(java.util.Scanner, java.lang.String, boolean);
LocalVariableTable:
Start Length Slot Name Signature
0 32 0 scanner Ljava/util/Scanner;
0 32 1 searchString Ljava/lang/String;
0 32 2 result Z
}
這基本上證實了上面看到的唯一區別 - m1
方法只分配了一個局部變量 - String line
。 它不會再創建任何對象,它只會再創建一個對任何一種方式分配的對象的引用 。
似乎不是主題,但這也會帶來性能。
while (!result && scanner.hasNextLine())
{
String line = scanner.nextLine();
result = line.indexOf(searchString) >= 0;
}
要回答你的問題,請執行scanner.nextLine().indexOf(searchString)
; 你期望nextLine()
做什么? 您希望在哪個對象上執行indexOf()
?
你可能已經猜到它依賴於String
; 所以,是的,這個String
被創建,是的,這個String
被使用。 這是(與你的猜測相反) 必要的 。
聲明變量( String s
)和賦值的操作與對象的實例化相比沒有任何成本( new String("test")
)。
換句話說,你想要實現的目標既不是有用的,也不是更高效的。
這里存在第二個問題,對於開發人員而言,這是一個更深層次的問題。 嘗試對這種代碼進行優化,沒有遇到實際問題,並且沒有任何明顯跡象表明此代碼可能會使您的應用程序運行速度明顯變慢,這簡直為時過早 。
大多數情況下,它會分散您想要實現的目標,並且會導致您為了優化而編寫不可讀的代碼(甚至可能根本不是優化!)。
在你的特殊情況下(我很驚訝之前沒有人提到它,這就是我寫這個答案的原因)你的“優化”代碼會讓每個人的生活變得更糟。
想象一下,你的代碼運行了,有些東西都失敗了,你在這一行得到一個NullPointerException
:
result = scanner.nextLine().indexOf(searchString) >= 0;
相反,有什么失敗了,您現在有一個清晰的思路的手動調試代碼,以找出是否scanner
是null
或者如果由於某種原因nextLine()
返回null
。
這個問題在您之前的代碼中甚至不存在,但是這種早期優化和嘗試使代碼更緊湊以避免等待一些操作的願望現在使您的代碼全局變得更糟。
編譯器無論如何都會內聯局部變量行,因此兩者之間沒有區別
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.