2013-01-04 38 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)将识别大量使用的部分(热点)并积极优化它们。最简单最简单的事情之一就是内联简单访问器。 –