3

異なるタイプのノード オブジェクトを持つ複数のノードを持つ N 分木を作成しようとしている [国 | State etc] から、以下のジェネリッククラスを修正してみました -

https://github.com/vivin/GenericTree/blob/master/src/main/java/net/vivin/GenericTreeNode.java

私は次のことを試しました -

package com.mycompany.ds;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class GenericTreeNode<T>{

    private T data;
    private List<GenericTreeNode<? super T>> children;
    private GenericTreeNode<? super T> parent;

    public GenericTreeNode() {
        super();
        children = new ArrayList<GenericTreeNode<? super T>>();
    }

    public GenericTreeNode(T data) {
        this();
        setData(data);
    }

    public GenericTreeNode<? super T> getParent() {
        return this.parent;
    }

    public List<GenericTreeNode<? super T>> getChildren() {
        return this.children;
    }

    public int getNumberOfChildren() {
        return getChildren().size();
    }

    public boolean hasChildren() {
        return (getNumberOfChildren() > 0);
    }

    public void setChildren(List<GenericTreeNode<? super T>> children) {
        for(GenericTreeNode<? super T> child : children) {
           child.parent = this;
        }

        this.children = children;
    }

    public void addChild(GenericTreeNode<? super T> child) {
        child.parent = this;
        children.add(child);
    }

    public void addChildAt(int index, GenericTreeNode<T> child) throws IndexOutOfBoundsException {
        child.parent = this;
        children.add(index, child);
    }

    public void removeChildren() {
        this.children = new ArrayList<GenericTreeNode<? super T>>();
    }

    public void removeChildAt(int index) throws IndexOutOfBoundsException {
        children.remove(index);
    }

    public GenericTreeNode<? super T> getChildAt(int index) throws IndexOutOfBoundsException {
        return children.get(index);
    }

    public T getData() {
        return this.data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public String toString() {
        return getData().toString();
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
           return true;
        }
        if (obj == null) {
           return false;
        }
        if (getClass() != obj.getClass()) {
           return false;
        }
        GenericTreeNode<?> other = (GenericTreeNode<?>) obj;
        if (data == null) {
           if (other.data != null) {
              return false;
           }
        } else if (!data.equals(other.data)) {
           return false;
        }
        return true;
    }

    /* (non-Javadoc)
    * @see java.lang.Object#hashCode()
    */
    @Override
    public int hashCode() {
       final int prime = 31;
       int result = 1;
       result = prime * result + ((data == null) ? 0 : data.hashCode());
       return result;
    }

    public String toStringVerbose() {
        String stringRepresentation = getData().toString() + ":[";

        for (GenericTreeNode<? super T> node : getChildren()) {
            stringRepresentation += node.getData().toString() + ", ";
        }

        //Pattern.DOTALL causes ^ and $ to match. Otherwise it won't. It's retarded.
        Pattern pattern = Pattern.compile(", $", Pattern.DOTALL);
        Matcher matcher = pattern.matcher(stringRepresentation);

        stringRepresentation = matcher.replaceFirst("");
        stringRepresentation += "]";

        return stringRepresentation;
    }
}

しかし、次のメソッドのエラー -

 public void setChildren(List<GenericTreeNode<? super T>> children) {
        for(GenericTreeNode<? super T> child : children) {
           child.parent = this;
        }

        this.children = children;
    }

    public void addChild(GenericTreeNode<? super T> child) {
        child.parent = this;
        children.add(child);
    }

エラー -

1 - Type mismatch: cannot convert from GenericTreeNode<T> to GenericTreeNode<? super capture#2-of ? super 
 T>

2 - Type mismatch: cannot convert from GenericTreeNode<T> to GenericTreeNode<? super capture#4-of ? super 
 T>

これらを修正するにはどうすればよいですか?

4

3 に答える 3

0

ditkin の答えに基づいて構築するには: すべてのクラスで GISEntity を実装または拡張した後、ツリーを次のように記述します。

public class GenericTreeNode<T extends GISEntity>{

    private T data;
    private List<GenericTreeNode<? extends GISEntity>> children;
    private GenericTreeNode<? extends GISEntity> parent;

    public GenericTreeNode() {
        super();
        children = new ArrayList<GenericTreeNode<? extends GISEntity>>();
    }

    ////////
    ......
    ////////

    public void addChild(GenericTreeNode<? extends GISEntity> child) {
        child.parent = this;
        children.add(child);
    }

    public void addChildAt(int index, GenericTreeNode<? extends GISEntity> child) throws IndexOutOfBoundsException {
        child.parent = this;
        children.add(index, child);
    }

    ////////
    ......
    ////////

}

クラスのキャストを回避しても実際には役に立たないことに注意してください。問題は、ノードに子を追加するとすぐに、それらを取得するときに、タイプ消去のために、それらが GISEntity であることがわかるということです。したがって、この手法では型の安全性が少ししか得られません。

于 2012-12-31T22:53:25.220 に答える
0

GISEntity を表すクラス/インターフェースを作成し、ジェネリック型 T が GISEntity を拡張するジェネリック ツリー ノードを作成できます。これにより、国/州など、さまざまな種類の GISEntity サブクラスのノードを持つことができます。

于 2012-12-31T22:14:49.620 に答える
0

異なるタイプのオブジェクトを同じコレクションに格納するために Generic を使用することはお勧めできません。すべきことは、階層を作成し、それを使用してオブジェクトを格納することです。適切な設計では、キャストせずにさまざまなオブジェクトにアクセスするために必要なすべてのものが基本クラスに含まれます。そうしないと、あちこちにキャストを記述する必要があります。コードの例を次に示します (ここでの設計は最適とはほど遠いものであり、単に仮想関数とポリモーフィズムの使用を示すためのものであることに注意してください)。

static class GISEntity {
    final String name;
    public GISEntity (String name) { this.name = name; }
    public String getName() { return name; }
    public String getTypeName() { return "GISEntity"; }
    public String toString() { return name; }
}
//
static class Country extends GISEntity {
    final String typeName = "country";
    public Country (String name) { super(name); }
    public String getTypeName() { return typeName; }
    public String toString() { return name; }
}
//
static class State extends GISEntity {
    public State (String name) { super(name); }
    public String getTypeName() { return "state"; }
    public String toString() { return name; }
}
//
static class Territory extends GISEntity {
    public Territory (String name) { super(name); }
    public String getTypeName() { return "territory"; }
    public String toString() { return name; }
}
//
// Here's an example of subclassing GenericTreeNode<GISEntity>:
//
static class IsATerritory extends GenericTreeNode<GISEntity> {

    IsATerritory (String name) { super (new Territory (name)); }

    public GISEntity getData() {
        State s = new State (super.getData().getName().toUpperCase());
        return s; }
};
//
// Here we put some data. Note that the order of insertion is important
// for the tree and that it's not alphabetical in this example.
//
GenericTree<GISEntity> earth = new GenericTree<GISEntity>() ;
//
GenericTreeNode<GISEntity> ListOfCountries = new GenericTreeNode<GISEntity>(new GISEntity("List of countries"));
//
GenericTreeNode<GISEntity> US = new GenericTreeNode<GISEntity>(new Country("United States"));
GenericTreeNode<GISEntity> Washington = new GenericTreeNode<GISEntity>(new State("Washington"));
GenericTreeNode<GISEntity> Florida = new GenericTreeNode<GISEntity>(new State("Florida"));
//
GenericTreeNode<GISEntity> Canada = new GenericTreeNode<GISEntity>(new Country("Canada"));
//
// We are now using some different ways for creating the nodes:
//
@SuppressWarnings("unchecked")
List<GenericTreeNode<GISEntity>> CanadaProvinces = new ArrayList<GenericTreeNode<GISEntity>>(
Arrays.asList(new GenericTreeNode<GISEntity>(new State("Quebec")), 
    new GenericTreeNode<GISEntity>(new State("Ontario")))
);
//
US.addChild(Washington);
US.addChild(Florida);
//
// Here's are two examples of subclassing; this time with anonymous classes.
// Don't forget that these two anonymous classes will hold an hidden reference
// to the outer classe as they are not static!
//
GenericTreeNode<GISEntity> alberta = new GenericTreeNode<GISEntity>() {
    { setData(new State ("Alberta")); }

    public GISEntity getData() {
        State s = new State (super.getData().getName().toUpperCase());
        return s; 
      }
};
//
GenericTreeNode<GISEntity> saskatchewan = new GenericTreeNode<GISEntity>(new State ("saskatchewan")) {
    public GISEntity getData() {
        State s = new State (super.getData().getName().toUpperCase());
        return s; }
};
//
CanadaProvinces.add(alberta);
CanadaProvinces.add(saskatchewan);
//
// Other ways for creating the nodes:
CanadaProvinces.add(new GenericTreeNode<GISEntity>(new State("Manitoba")));
//
// Note the use of the IsATerritory subclass:
CanadaProvinces.add(new IsATerritory("Northwest Territories"));
//
Canada.setChildren(CanadaProvinces);
//
ListOfCountries.addChild(Canada);
ListOfCountries.addChild(US);
//
earth.setRoot(ListOfCountries);
//
System.out.println(earth.toString());
System.out.println();
System.out.println(earth.toStringWithDepth());
System.out.println();
System.out.println(ListOfCountries.toStringVerbose());
//
List<GenericTreeNode<GISEntity>> loc = earth.build(GenericTreeTraversalOrderEnum.PRE_ORDER);
System.out.println(loc);
//
Map<GenericTreeNode<GISEntity>, Integer> locd = earth.buildWithDepth(GenericTreeTraversalOrderEnum.PRE_ORDER);
System.out.println(locd);
//
Map<GenericTreeNode<GISEntity>, Integer> locd2 = earth.buildWithDepth(GenericTreeTraversalOrderEnum.POST_ORDER);
System.out.println(locd2);
//
// Two examples of iteration; showing both the use of the instanceof operator
// and of virtual (or override) functions:
// 
for (GenericTreeNode<GISEntity> gen: loc) {
    GISEntity data = gen.getData();

    if (data instanceof State) {
        System.out.println("Is State: " + data.getName());
    } else if (data instanceof Country) {
        System.out.println("Is Country: " + data.getName());
    } else {
        System.out.println(data.getTypeName() + data.getName());
    }
}
//
for (Entry<GenericTreeNode<GISEntity>, Integer> entry: locd.entrySet()) {
    GISEntity data = entry.getKey().getData();
    Integer depth = entry.getValue();

    if (data instanceof State) {
        System.out.println(depth.toString() + ": Is State: " + data.getName());
    } else if (data instanceof Country) {
        System.out.println(depth.toString() + ": Is Country: " + data.getName());
    } else {
        System.out.println(depth.toString() + ": " + data.getTypeName() + data.getName());
    }
}

この例では、クラス GenericTreeNode を 3 つの異なる方法 (2 つの匿名クラス、1 つは名前付きクラス) でサブクラス化し、getData を変更して、名前が大文字のコピーに置き換えられた新しい GISEntity を返すようにしました。

これらの 3 つのサブクラスはすべて、私が使用GenericTreeNode<GISEntity>しているものであり、のようなものではないことに注意してくださいGenericTreeNode<Territory>。これは、Territoryが のサブクラスであっても、 のサブクラスでGISEntryGenericTreeNode<Territory>ないためですGenericTreeNode<GISEntry>

GenericTreeNode<Territory>との混合のようなものGenericTreeNode<GISEntry>を使用するには、 and を使用する必要が? extends GISEntryあり? super GISEntry、これにより汎用コードの複雑さが 1000 倍になります。GenericTree<>ジェネリック クラスおよびの重いサブクラス化を行いたい場合を除き、この型GenericTreeNode<>を使用してもまったく役に立ちません。?さまざまな種類のオブジェクトを収集する場合でも。汎用コードで何年もの経験がない限り、この?表記法は使用しないでください。ほとんどのプロジェクトは、より単純な汎用コードで問題なく動作します。

また、興味のある方のためbuild()に、関数と関数の両方について、ジェネリック ツリーに反復処理の例をいくつか追加しました。buildWithDepth()

最後に、参考までに、この汎用ツリーについてはhttp://vivin.net/2010/01/30/generic-n-ary-tree-in-java/ (3 ページ) で説明しています。

于 2013-01-01T12:36:34.617 に答える