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();

「ジャンプ」が少ないため、2番目のバージョンの方が高速ですか?

4

5 に答える 5

9

それは完全にgetXYZ呼び出しが何をするかに依存します。それらが基礎となるフィールドへの単純なアクセサーである場合は、いいえ、HotSpot(OracleのJVM)ではありません。必要に応じて最適化されるためです。一方、複雑な作業(btreeのトラバースなど)を行う場合は、もちろんその作業を繰り返し行う必要があります(HotSpotが呼び出しがべき等であることを証明できない限り、コードが複雑になる可能性は低くなります)。(彼らが繰り返し作業を行うことが重要かどうかは別の質問です。実際のパフォーマンスの問題が発生するまで/発生しない限り、心配する必要はありません。)

しかし、2つ目は、はるかに読みやすく、保守しやすいものです。それがそれを使用するより強力な理由です。

于 2013-01-04T06:19:23.090 に答える
4

パフォーマンスではなく、私が見るsecond as better human understandable code. マイクロ最適化について心配する必要はありませんが、適切でクリーンなコードを記述してください。

于 2013-01-04T06:16:57.197 に答える
2

あなたが考えている最適化は、時期尚早の最適化と呼ばれます。本当に必要でない限り、これらについて考えるべきではありません。

2番目に読みやすいという@AmitDの回答に同意します。このようにメソッド呼び出しを連鎖させる場合、次のように書くこともできます -

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

可読性をさらに向上させます。

于 2013-01-04T06:21:31.643 に答える
0

おそらくそうです。しかし、ゲッターが内部で通常のゲッターのように見えると仮定すると、おそらくそれは非常に小さいので、測定することはほとんど不可能になります。

また、このコードを頻繁に実行して問題が発生する場合は、Hotspotコンパイラの魔法が働き始めてバイトコードを壊し、おそらく両方のバリエーションを同じにします。

結局、実際に何が起こるかを知ることは非常に困難です。パフォーマンスが重要な場合は、テストを設定します。テストのコストを正当化するのにパフォーマンスが十分に重要でない場合...まあ、心配するほど重要ではありません。

于 2013-01-04T06:20:24.787 に答える
0

バイトコード分析を使用するか、 を使用して 2 つのアプローチの時間を計りますSystem.nanoTime。2番目の方が速いと思います。これを結論付けるために私がしたことは次のとおりです。

以下に示すように、3つのクラスを作成しました。

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

次に、2 つの単純なメソッドを作成し、javap -c CLASS_NAME.

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ます。そして、フィールドとフィールドの両方にアクセスするために再度呼び出されるこの同じサイクルのコンパイラによる最適化はありませんでした。nnnameage

次に、2 番目の方法を示します。

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ます。したがって、これは以前のものよりも高速である必要があると思います。

于 2013-01-04T06:45:06.603 に答える