これは、質問というよりも共有したかった落とし穴です。 で印刷する場合toString()
、Java はコレクション内の直接サイクル (コレクションがそれ自体を参照する場合) を検出しますが、間接サイクル (コレクションが別のコレクションを参照する別のコレクションを参照する場合) は検出しません。最初のもの - またはより多くのステップで)。
import java.util.*;
public class ShonkyCycle {
static public void main(String[] args) {
List a = new LinkedList();
a.add(a); // direct cycle
System.out.println(a); // works: [(this Collection)]
List b = new LinkedList();
a.add(b);
b.add(a); // indirect cycle
System.out.println(a); // shonky: causes infinite loop!
}
}
コレクションを出力するコードのデバッグ中に発生したため、これは私にとって本当に落とし穴でした (直接サイクルをキャッチしたときに驚いたので、一般的にチェックを実装したと誤って想定しました)。質問があります:なぜですか?
私が考えることができる説明は、それ自体を参照するコレクションをチェックするのは非常に安価であるということです.コレクションを保存するだけで済みます.出会い、根本から。さらに、ルートが何であるかを確実に伝えることができない場合があるため、すべてのコレクションをシステムに保存する必要がありますが、とにかくこれを行いますが、すべてのコレクション要素に対してハッシュルックアップを行う必要もあります。 . (ほとんどのプログラミングで) サイクルの比較的まれなケースでは、非常にコストがかかります。(私が思うに)直接サイクルをチェックする唯一の理由は、それがとても安いからです(1つの参照比較)。
わかりました...私は自分の質問にちょっと答えました-しかし、何か重要なことを見逃していませんか? 何か追加したい人はいますか?
明確化: 私が見た問題は、コレクション (つまり、メソッド)の印刷に固有のものであることがわかりました。サイクル自体toString()
には問題はありません(私はサイクルを自分で使用しており、必要です)。問題は、Java がそれらを印刷できないことです。Edit Andrzej Doyle は、コレクションだけでなく、呼び出されるすべてのオブジェクトであると指摘しています。toString
このメソッドに制約されていることを考えると、これをチェックするアルゴリズムは次のとおりです。
- ルートは、最初に呼び出されるオブジェクトです
toString()
(これを決定するには、toString が現在進行中かどうかの状態を維持する必要があるため、不便です)。- 各オブジェクトをトラバースするときに、それを一意の識別子 (インクリメントされたインデックスなど) と共に IdentityHashMap に追加します。
- ただし、このオブジェクトが既にマップにある場合は、代わりにその識別子を書き出します。
このアプローチは、multirefs (複数回参照されるノード) も正しくレンダリングします。
メモリ コストは IdentityHashMap (オブジェクトごとに 1 つの参照とインデックス) です。複雑さのコストは、有向グラフのすべてのノード (つまり、出力される各オブジェクト) のハッシュ ルックアップです。