3

在 Java 中,如果我不断使用嵌套的 get 来检索值,是否会影响性能?例如:

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

对比:

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

第二个版本会更快,因为它的“跳跃”更少吗?

4

5 回答 5

9

这完全取决于getXYZ调用的作用。如果它们是底层字段的简单访问器,那么不,不在 HotSpot(Oracle 的 JVM)上,因为如果有任何需要,它们会被优化掉。另一方面,如果他们做任何类型的复杂工作(遍历 btree 等),那么他们当然必须重复做这项工作(除非 HotSpot 可以向自己证明调用是幂等的,如果代码有任何复杂性变得不太可能)。(他们重复做这项工作是否重要是另一个问题;直到/除非你看到实际的性能问题,不要担心。)

但第二个更具可读性和可维护性。这是使用它的更强有力的理由。

于 2013-01-04T06:19:23.090 回答
4

而不是我看到的性能second as better human understandable code。你不应该担心微优化,而是编写一个好的和干净的代码。

于 2013-01-04T06:16:57.197 回答
2

您正在考虑的优化称为过早优化。你不应该考虑这些,除非你真的必须这样做。

I agree with @AmitD's answer about being the second one more readable. When chaining method calls like this, you can also write them in the following way -

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

to further improve readability.

于 2013-01-04T06:21:31.643 回答
0

可能是的。但假设吸气剂在内部看起来像普通吸气剂,它可能太小以至于几乎无法测量。

此外,如果您经常运行此代码以使其重要,Hotspot 编译器的魔力将发挥作用并破坏字节码,可能再次使两种变体相同。

最后,很难说到底会发生什么。如果性能对您很重要,请设置测试。如果性能不足以证明测试成本的合理性......那么担心也没有关系。

于 2013-01-04T06:20:24.787 回答
0

Either use byte code analysis or time the two approaches using System.nanoTime. I think second one is faster. here is what I did to conclude this:

I wrote three classes as given below:

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;
}

Then I wrote two simple methods and get their java byte code using javap -c CLASS_NAME.

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

The byte code of above method is:

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

You can clearly see at byte code level, each time you try to access field, it put the value of that filed on stack and then this cycle continues. Therefore, a.a1.a2....an would be n instruction if stack would have enough spae to hold all n. And there was no optimisation by compiler this same cycle of called again to access both name and age field.

Now here is the second method:

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

Byte code for above method is:

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        

Above is 4 instruction shorter than previous code as it prevents execution of getfield. So I think this should be faster than previous one.

于 2013-01-04T06:45:06.603 回答