2013-01-04 63 views
3

在Java中,如果我不斷使用嵌套get來檢索值,那麼會出現性能問題嗎?例如:嵌套獲取性能

String firstname = getOffice().getDepartment().getEmployee().getFirstName(); 
String lastname = getOffice().getDepartment().getEmployee().getLastName(); 
String address = getOffice().getDepartment().getEmployee().getAddress(); 

VS:

Employee e = getOffice().getDepartment().getEmployee(); 
String firstname = e.getFirstName(); 
String lastname = e.getLastName(); 
String address = e.getAddress(); 

請問第二版是更快,因爲它具有較小的 '跳'?

+1

你正在尋找的詞是'鏈接'獲取。嵌套意味着一個獲得正在另一個環境中發生。 – Perception

回答

9

這完全取決於getXYZ叫做做什麼。如果他們是基礎字段的簡單訪問者,那麼不是,而不是HotSpot(Oracle的JVM),因爲如果需要的話,他們會得到優化。另一方面,如果他們做了任何複雜的工作(遍歷一棵樹等),那麼他們當然必須反覆做這些工作(除非HotSpot可以證明自己的調用是冪等的,如果代碼具有任何複雜性變得不太可能)。 (無論是重要他們反覆做工作是另外一個問題,直到/除非你看到實際的性能問題,不用擔心。)

但第二個是可讀性和可維護性。這是使用它的更強大的原因。

4

而不是性能我看到second as better human understandable code。你不應該擔心微型優化,而是寫一個好的,乾淨的代碼。

0

可能是的。但假設吸氣者看起來像是普通的吸氣者,它可能會很小,幾乎不可能測量。

此外,如果你經常通過這段代碼來證明它的重要性,那麼Hotspot編譯器的魔力就會涌入並破壞字節碼,可能會再次使這兩個變體相同。

最後,很難分辨真的會發生什麼。如果績效對你來說很重要,那就設置一個測試如果性能不足以證明測試的成本是正確的,那麼它無所謂擔心。

2

您正在考慮的優化稱爲premature optimization。除非你真的需要,否則你不應該考慮這些。

我同意@AmitD關於作爲第二個更具可讀性的答案。當鏈接這樣的方法調用時,您還可以按照以下方式編寫它們 -

Employee e = getOffice() 
       .getDepartment() 
       .getEmployee(); 
String firstname = e.getFirstName(); 
String lastname = e.getLastName(); 
String address = e.getAddress(); 

進一步提高可讀性。

+2

+1爲過早優化參考 – Krease

0

要麼使用字節碼分析,要麼使用System.nanoTime這兩種方法計時。我認爲第二個更快。這裏是我做過什麼來結束這:

我寫了三個類,如下所示:

public static class A { 
    public B b = new B(); 
} 

public static class B { 
    public E e = new E(); 
} 

public static class E { 
    public String name = "s"; 
    public int age = 1; 
} 

然後我寫了兩個簡單的方法,並使用javap -c CLASS_NAME得到他們的Java字節碼。

public static void Test1() { 
    A a = new A(); 
    String str = a.b.e.name; 
    int age = a.b.e.age; 
} 

上述方法的字節碼是:

public static void Test1(); 
    Code: 
     // new A(); 
     0: new   #15 
     3: dup   
     4: invokespecial #17     
     7: astore_0  
     8: aload_0 

     // a.b (it accesses the field and put it on operand stack) 
     9: getfield  #18 

     // b.e 
     12: getfield  #22 

     // b.name 
     15: getfield  #28 

     // pop 'name' from stack 
     18: astore_1  
     19: aload_0 

     // cyle continues  
     20: getfield  #18 
     23: getfield  #22 
     26: getfield  #34 
     29: istore_2  
     30: return 

您可以在字節代碼級清楚地看到,在每次嘗試訪問現場時,它就把這個值上堆申請,然後這個循環繼續。因此,a.a1.a2....an將是n指令,如果堆棧將有足夠的空間來容納所有n。並且編譯器沒有優化再次調用同一個週期來訪問nameage字段。

現在這裏是第二種方法:對上述方法

public static void Test2() { 
     A a = new A(); 
     E e = a.b.e; 
     String str = e.name; 
     int age = e.age; 
    } 

字節代碼是:

public static void Test2(); 
    Code: 
     // new A(); 
     0: new   #15 
     3: dup   
     4: invokespecial #17 
     7: astore_0  
     8: aload_0  

     // store a.b.e on operand stack once 
     9: getfield  #18 
     12: getfield  #22 
     15: astore_1  
     16: aload_1 

     // get 'name' field 
     17: getfield  #28 
     20: astore_2  
     21: aload_1  
     // get 'age' field 
     22: getfield  #34 
     25: istore_3  
     26: return   

以上是4個指令比以前更短的代碼,因爲它防止了getfield執行。所以我認爲這應該比以前更快。

+0

努力的努力。它非常令人大開眼界。 – delita

+0

但請記住,字節碼只是故事的一部分。 JVM可以並且將在運行時改變它。 Oracle的JVM(HotSpot)將識別大量使用的部分(熱點)並積極優化它們。最簡單最簡單的事情之一就是內聯簡單訪問器。 –