5

Joshua Bloch 著の「Effective JAVA」で、静的ファクトリ メソッドについて読んでいたときに、次のような記述がありました。

繰り返し呼び出しから同じオブジェクトを返す静的ファクトリ メソッドの機能により、クラスは常にどのインスタンスが存在するかを厳密に制御できます。これを行うクラスは、インスタンス制御であると言われています。インスタンス制御クラスを作成する理由はいくつかあります。インスタンス制御により、クラスはそれがシングルトン (項目 3) またはインスタンス化不可能 (項目 4) であることを保証できます。また、不変クラス (項目 15) が、2 つの等しいインスタンスが存在しないことを保証できるようにします: a.equals(b) は、a==b の場合のみです。クラスがこれを保証する場合、そのクライアントは equals(Object) メソッドの代わりに == 演算子を使用できるため、パフォーマンスが向上する可能性があります。列挙型 (項目 30) は、この保証を提供します。

== 演算子がどのようにパフォーマンスの向上をもたらすかを調べるために、 String.javaを調べる必要がありました。

このスニペットを見ました

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String) anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                            return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

パフォーマンスの向上によって、彼はここで何を意味しますか? それがどのようにパフォーマンスの向上をもたらすか。

彼は次のように言うつもりですか

すべてのクラスが a==b の場合にのみ a.equals(b) を保証できる場合、それは、2 つの異なるメモリ空間を参照するオブジェクトが存在できず、メモリである同じ data を保持することができないという間接的な要件をもたらすことを意味します。無駄。それらが同じデータを保持している場合、それらは1つの同じオブジェクトです。つまり、同じメモリ位置を指しています。

私はこの推論で正しいですか?

私が間違っている場合は、これを理解するために私を導くことができますか?

4

6 に答える 6

3

引用された部分が意味することは、不変クラスがそのインスタンスをインターンすることを選択できるということです。これは、Guava の を介して簡単に実装できInternerます。次に例を示します。

public class MyImmutableClass {
    private static final Interner<MyImmutableClass> INTERN_POOL = Interners.newWeakInterner();
    private final String foo;
    private final int bar;

    private MyImmutableClass(String foo, int bar) {
        this.foo = foo;
        this.bar = bar;
    }

    public static MyImmutableClass of(String foo, int bar) {
        return INTERN_POOL.intern(new MyImmutableClass(foo, bar));
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(foo, bar);
    }

    @Override
    public boolean equals(Object o) {
        if (o == this)
            return true;        // fast path for interned instances
        if (o instanceof MyImmutableClass) {
            MyImmutableClass rhs = (MyImmutableClass) o;
            return Objects.equal(foo, rhs.foo)
                    && bar == rhs.bar;
        }
        return false;
    }
}

ここでは、コンストラクターがプライベートになります。すべてのインスタンスは、新しいインスタンスが既存のインスタンスに対するものである場合、代わりに既存のインスタンスが返されるようにするためMyImmutableClass.of()に を使用するファクトリ メソッドを使用する必要があります。Internerequals()

インターンは、不変オブジェクトに対してのみ使用できます。これは、監視可能な状態 (つまり、外部からアクセス可能なすべてのメソッドの動作、特にequals()and hashCode()) がオブジェクトの存続期間にわたって変化しないオブジェクトを意味します。可変オブジェクトをインターンすると、インスタンスが変更されたときに動作が正しくなくなります。

他の多くの人がすでに述べているように、たとえ不変であっても、どのオブジェクトをインターンするかを慎重に選択する必要があります。インターンされた値のセットが、重複する可能性のある数に比べて小さい場合にのみ実行してください。たとえば、Integer可能な値は 40 億を超えるため、一般的にインターンする価値はありません。しかし、最も一般的に使用されるInteger値をインターンする価値があり、実際にInteger.valueOf()は -128 から 127 の間の値をインターンします。一方、列挙型はインターンに適しています (定義上、インターンされます)。可能な値のセットは次のとおりです。小さな。

一般に、ほとんどのクラスでは、インターンを保証するのに十分な重複があるかどうかを判断するためjhatに (または、自分のプロジェクトをプラグインするにはfasthatを使用して) ヒープ分析を行う必要があります。それ以外の場合は、シンプルに保ち、インターンをしないでください。

于 2013-11-17T05:29:45.897 に答える
3

すべてのクラスが a==b の場合にのみ a.equals(b) を保証できる場合、それは、2 つの異なるメモリ空間を参照するオブジェクトが存在できず、メモリである同じ data を保持できないという間接的な要件をもたらすことを意味します。無駄。それらが同じデータを保持している場合、それらは1つの同じオブジェクトです。つまり、同じメモリ位置を指しています。

はい、それは著者が運転しているものです。

(特定のクラスでは、これはすべてのクラスで可能ではありません。特に、可変クラスでは機能しません) (動的にディスパッチされるメソッド呼び出し)の==代わりに(単一の JVM オペコード)を呼び出すことができればequals、保存されます(一部)オーバーヘッド。

enumたとえば、 s の場合はこのように機能します。

そして、誰かがメソッドを呼び出したとしても(これは防御的なプログラミングの良い方法です。IMHOオブジェクトequalsに使用する習慣を身につけたくありません)、そのメソッドは(潜在的に複雑なものを見る必要があるのではなく)単純なものとして実装できますオブジェクトの状態)。====

ちなみに、「通常の」equals メソッド (String など) の場合でも、最初にオブジェクトの ID を確認し、次にオブジェクトの状態を簡単に確認することを実装することをお勧めします (これは String#equals が行うことです。分かった)。

于 2013-11-17T05:24:08.517 に答える
1

オブジェクトの 2 つのインスタンスが存在せず、セマンティック値が同等であることを保証できる場合 (つまり、 ifxyが異なるインスタンス [ x != y]を参照しx.equals(y) == false、すべてのxとを参照する場合y)、これは、単純に 2 つの参照のオブジェクトが等しいかどうかを比較できることを意味します。それらが同じインスタンスを参照しているかどうかを確認します==

の実装は、==基本的に 2 つの整数 (メモリ アドレス) を比較するだけであり、通常、事実上すべての重要な の実装よりも高速です.equals()

a の 2 つのインスタンスが等しくないことを保証できないため、これは sに対して行うことができるジャンプではないことに注意してください。StringString

String x = new String("hello");
String y = new String("hello");

であるため、等価性をチェックx != y && x.equals(y)するだけでは十分ではありません。x == y

于 2013-11-17T05:24:17.923 に答える
0

不変オブジェクトへの参照を使用して複雑な値がカプセル化されている場合、一般に、2 つの参照を比較するときに発生する可能性のある 3 つのシナリオがあります。

  • それらは同じオブジェクトへの参照です(非常に高速)

  • それらは、異なる値をカプセル化する異なるオブジェクトへの参照です(多くの場合高速ですが、低速です)。

  • それらは同じ値をカプセル化する異なるオブジェクトへの参照です (通常は常に遅い)

オブジェクトがほとんど同じであることが判明する場合、ケース 3 の頻度を最小限に抑えることにはかなりの価値があります。あまり頻繁に起こらないでください。

任意の値に対して、その値を保持するオブジェクトが 2 つ以上存在しないことを確認した場合、2 つの参照が異なるオブジェクトを識別することを観察するコードは、問題の値を実際に調べる必要なく、それらが異なる値をカプセル化していると推測できます。ただし、これを行う価値は、多くの場合、いくらか制限されています。問題のオブジェクトが大規模で複雑なネストされたコレクションであり、非常に類似している場合、各コレクションでその内容の 128 ビット ハッシュを計算してキャッシュすることができます。コンテンツが異なる 2 つのコレクションのハッシュ値が一致する可能性は低く、ハッシュ値が異なるコレクションはすぐに等しくないと認識される可能性があります。一方、一般的に同じコンテンツをカプセル化する参照を持つ同じオブジェクトへの識別は、同一のコレクションへの参照がいくつか存在する場合でも、それ以外の場合は常に悪い「等しい」ケースのパフォーマンスを向上させることができます。

別のインターン コレクションを使用したくない場合に使用できるアプローチは、各オブジェクトにlongシーケンス番号を保持させて、他の点では同一の 2 つのオブジェクトのどちらが最初に作成されたかを常に判断できるようにすることです。同じ内容を保持することが知られている最も古いオブジェクト。2 つの参照を比較するには、最初に、それぞれと同等であることがわかっている最も古いオブジェクトを特定します。最初のオブジェクトと一致することがわかっている最も古いオブジェクトが、2 番目のオブジェクトと一致することがわかっている最も古いオブジェクトと同じでない場合は、オブジェクトの内容を比較します。それらが一致する場合、一方が他方よりも新しくなり、そのオブジェクトは他方を「一致することが知られている最も古いオブジェクト」と見なすことができます。

于 2013-11-17T21:26:52.667 に答える
0

私はそれがこれを意味すると思います:

2 つの複雑な構造が等しいかどうかをテストする必要がある場合は、通常、それらが同じであることを確認するために多くのテストを行う必要があります。

しかし、言語のトリックのために、2 つの複雑だが等しい構造が同時に存在できないことがわかっている場合は、ビットごとに比較して等しいことを確認する代わりに、メモリ内の同じ場所にあることを確認し、次の場合に false を返すことができます。ではない。

誰でもオブジェクトを作成できる場合、同じであるが異なるインスタンスである2つのオブジェクトを作成できないことを保証することはできません..しかし、オブジェクトの作成を制御し、異なるオブジェクトのみを作成する場合、複雑な等価性は必要ありませんテスト。

于 2013-11-17T05:25:06.613 に答える