3

试图用不同类型的节点对象创建具有多个节点的 N 叉树[国家 | 状态等],我尝试从 -

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 进行了子类化,以更改 getData 以便它返回一个新的 GISEntity,其中名称已被其大写副本替换。

请注意,我正在使用所有这三个子类,GenericTreeNode<GISEntity>而不是类似GenericTreeNode<Territory>. 这是因为即使Territory是 的子类GISEntry,该类GenericTreeNode<Territory>也不是 的子类GenericTreeNode<GISEntry>

为了使用类似于 with 的混合GenericTreeNode<Territory>GenericTreeNode<GISEntry>我们必须使用? extends GISEntryand ? super GISEntry,这将使通用代码的复杂度增加一千倍。除非您想对泛型类GenericTree<>和进行一些繁重的子类化GenericTreeNode<>,否则使用该类型是完全没用的?;甚至对于收集不同类型的对象。除非您有多年的通用代码经验,否则不要使用该?符号。大多数项目都可以使用更简单的通用代码完全正常。

我还为感兴趣的人添加了一些在通用树上迭代的示例build()buildWithDepth()函数。

最后,作为参考,此通用树在http://vivin.net/2010/01/30/generic-n-ary-tree-in-java/(3 页)中进行了解释。

于 2013-01-01T12:36:34.617 回答