1

リフレクションを使用してオブジェクトのフィールドを検査し、その構造を再帰的に横断してすべてのメンバーのツリーを構築する小さな Java プログラムを作成しました。デバッガーが変数を検査するときと同様です。

大きな問題は、時々循環参照が現れることです。私のツリーがグラフになる!その後、私のプログラムは無限ループに入り、最終的に StackOverflow 例外をスローします (皮肉なことに!)

私の質問は...標準のグラフ横断アルゴリズムを実装するために、任意のオブジェクトを訪問済みとして「マーク」するにはどうすればよいですか? 任意のオブジェクトを入力として使用でき、異なるオブジェクトが同一のハッシュ コードを返す可能性があるため、hashCode() は使用できません。どんな手掛かり?

ありがとうございます!

public class ClassHierarchyItem {

private boolean parent;
private String id;
private String parentId;
private String name;
private String type;
private String value;

public ClassHierarchyItem(boolean parent, String id, String parentId, String name, String type, String value){
    this.parent = parent;
    this.id = id;
    this.parentId = parentId;
    this.name = name;
    this.type = type;
    this.value = value;
}

public String toString() {
    return (isParent() ? "+" : "") + name + " - " + type + " - " + value + " [id=" + id + ", pId=" + parentId + "]";  
}
//Getters and setters follow (cutted)
}

public class ClassHierarchyNavigator {

public static void main(String[] args) {
    Integer i = 123;

    // Fails with StackOverflow exception (some reference inside Integer points back to base object)
    System.out.println(renderHirearchy(i));
}

public static List<ClassHierarchyItem> renderHirearchy(Object o) {

    List<ClassHierarchyItem> items = new ArrayList<ClassHierarchyItem>();

    boolean parent = (o.getClass().getDeclaredFields().length > 0 && !o.getClass().isPrimitive() && o.getClass() != String.class)
            || o.getClass().isArray();

    buildObjectTree(items, o, parent, "root", o.getClass().getName(), "r", "");

    return items;
}

private static boolean isParent(Field field) {

    return (field.getClass().getDeclaredFields().length > 0 && !field.getType().isPrimitive() && field.getType() != String.class)
            || field.getType().isArray();
}

private static void buildObjectTree(List<ClassHierarchyItem> items, Object object, boolean parent,
        String objectName, String objectType, String objectId, String parentId) {

    long subItemCount = 1;
    String value = object == null ? "null" : object.toString();

    ClassHierarchyItem item = new ClassHierarchyItem(parent, objectId, parentId, objectName, objectType,
            value.substring(0, value.length() > 80 ? 80 : value.length()));
    items.add(item);

    if (!parent) {
        return;
    }

    // if (isArray) {
    // do_array_treatment
    // } else {
    for (Field field : object.getClass().getDeclaredFields()) {
        field.setAccessible(true);
        Object child;
        try {
            child = field.get(object);
        } catch (IllegalArgumentException e) {
            continue;
        } catch (IllegalAccessException e) {
            continue;
        }

        String childId = objectId + "-" + subItemCount++;
        String fieldName = field.getName();
        boolean childIsParent = child != null && !"this$0".equals(fieldName) && isParent(field);

        buildObjectTree(items, child, childIsParent, fieldName, field.getType().getName(), childId, objectId);
    }

}
}
4

1 に答える 1

0

HashMap などのハッシュテーブル データ構造を使用し、オブジェクトをキーとして使用します。確かに、いくつかのハッシュは衝突しますが、衝突の解決はハッシュテーブルの根底にある考え方です。すべてのハッシュは、同じハッシュを持つすべてのキーと値のペアを含むバケットにつながるため、オブジェクトを取得できます。

ただし、オブジェクトが equals() を再実装して 2 つのオブジェクトが等しい場合、衝突解決メソッドは同じバケットから 2 つのキーを区別できないため、これは問題です。その場合は、hashcode() を使用してハッシュを取得し、言語の "==" 比較を使用してオブジェクトをメモリ アドレスで比較することにより、独自の hastable を設計します。

于 2013-10-31T15:29:56.973 に答える