1

こんばんは。

かなり複雑な質問があります。Java を練習するために、標準ライブラリーのデータ構造のいくつかを再実装してきました。スタック、LinkedList、ツリーなど。非常に単純な例を使用して、またはメソッドが使用されjava.util.Stackたときにクラスがディープ コピーを実行することを確認しました。目的はクラスの内容を外部の干渉から保護することであるため、これは理解できます。これまでのところ、スタックの私自身の実装 (単純な配列を使用した素朴な実装、リンクされたリストは後で提供されます) では、これをまったく気にしませんでした:peek()pop()

public class ArrayStack<T> implements Stack<T> {
    private T[] data; // Will expand the array when stack is full.
    private int top; // serves as both top and count indicator.
    ...
    ...
   @Override
   public T pop() throws EmptyStackException {
    if(top == -1)
        throw new EmptyStackException("Stack is empty.");
    return data[top--]; // Shallow copy, dangerous!
}

残念ながら、ジェネリックはインスタンス化できないため、コピー コンストラクターを想定してreturn new T(data[top--]);、SO で見回したようなことを行うことはできませんclone()このスレッドは、クラスの署名を次のように拡張することを提案しています。

public class ArrayStack<T extends DeepCloneableClass> implements Stack<T>
...

whereDeepCloneableClassは、「ディープ クローニング」を可能にするインターフェイスを実装するクラスです (関連する詳細については、そのスレッドのトップの応答を参照してください)。Stringもちろん、この方法の問題点は、 のような標準クラスから、または私のそのカスタム クラスを拡張することを本当に期待できないことIntegerです。もちろん、既存のすべての jUnit テストは、コンパイル時に文句を言います。それらは整数と文字列のスタックに依存しているためです。したがって、このソリューションが実行可能であるかのようには感じません。

このスレッドは、ほぼすべてのオブジェクトのクローンを作成するためにサードパーティ ライブラリを使用することを提案しています。このライブラリはまだサポートされているように見えますが (最新のバグ修正は 1 か月以内に行われています)、サードパーティのツールに依存せず、Java が提供してくれるものは何でも使用したいと思います。この理由は、これらの ADT のソース コードがいつか大学生と共有される可能性があるためです。余分なツールをインストールする負担を彼らに負わせたくないからです。

pop()したがって、peek()popFront()、 などのメソッドへの単純なインターフェースを可能にしながら、汎用 Java データ構造の内部整合性を維持するための単純で、可能であれば効率的な方法を探しています。

助けてくれてどうもありがとう!

ジェイソン

4

4 に答える 4

0

コメントは、私が間違っていたことを理解するのに役立ちました。次の例を使用して、コレクション内のオブジェクトへの参照を提供するときにJava標準ライブラリのコレクションがディープコピーを行うことを自分自身や他の人に「証明」しました。

import java.util.Stack;

public class StackTestDeepCopy {
    public static void main(String[] args){
        Stack<String> st = new Stack<String>();
        st.push("Jim");
        st.push("Jill");
        String top = st.peek();
        top = "Jack";
        System.out.println(st); 
    }
}

st を印刷すると、オブジェクトが変更されていないことがわかり、ディープ コピーが行われたと結論付けました。違う!Strings は不変であり、したがって、ステートメントtop = "Jack"は を変更しませんString(そのObjectようなステートメントによって「変更」されるわけではありませんが、私は正直に考えていませんでした)、単に参照ポイントを新しい場所にします。ヒープ。実際に変更可能なクラスを含む新しい例は、私のやり方でエラーを理解させてくれました。

この問題が解決された今、標準ライブラリがこれを可能にしているという事実に、私はかなり困惑しています。標準ライブラリの要素へのアクセスが浅いコピーとして実装されているのはなぜですか? 非常に危険に聞こえます。

于 2013-09-27T05:08:50.100 に答える
-1

java.util.Stackディープコピーを行いません:

import java.util.Stack;
public class Test {
    String foo;
    public static void main(String[] args) {
        Test test = new Test();
        test.foo = "bar";
        Stack<Test> stack = new Stack<Test>();
        stack.push(test);
        Test otherTest = stack.pop();
        otherTest.foo = "wibble";
        System.out.println("Are the same object: "+(test.foo == otherTest.foo));
    }
}

結果:

Are the same object: true

コピーを実行した場合、test と otherTest は別のオブジェクトを指します。典型的なスタック実装は、コピーではなく、スタックに追加された同じオブジェクトへの参照を返すだけです。


おそらく、返す前に配列項目を null に設定することもできます。そうしないと、配列はオブジェクトへの参照を保持したままになります。

于 2013-09-27T05:09:43.010 に答える