2

我正在尝试扩展AbstractMap以创建一个MapTreeNode类(通过键而不是索引访问子节点的树节点)。

我已经有一种方法可以让一组孩子正常工作:

public class MapTreeNode<K,V> implements Map.Entry<K,V> {
    private Map<K,MapTreeNode<K,V>> children = new HashMap<K,MapTreeNode<K,V>>();
    private Set<MapTreeNode<K,V>> child_set = null;

    public Set<MapTreeNode<K,V>> children() {
        if (child_set == null)
            child_set = new ChildSet();

        return child_set;
    }

    ...

    private final class ChildSet extends AbstractSet<MapTreeNode<K,V>> {
        @Override
        public Iterator<MapTreeNode<K,V>> iterator() {
            return children.values().iterator();
        }

        @Override
        public int size() {
            return MapTreeNode.this.childCount();
        }
        ...
    }

}

我想创建一个节点(Map<K,V>)的地图视图并重用child_set,但我不确定 Java 的泛型是否可行:

public Map<K,V> asMap() {
    return new AbstractMap<K,V>() {
        @Override
        public Set<Map.Entry<K,V>> entrySet() {
            return child_set; // line 166
        }
    };
}

这当然给

MapTreeNode:166: incompatible types
found   : java.util.Set<MapTreeNode<K,V>>
required: java.util.Set<java.util.MapEntry<K,V>>

有没有办法可以为此重用我的ChildSet课程?

4

5 回答 5

3

问题在于entrySet()' 的返回类型。它是Set<Map.Entry<K,V>>。如您所知,对于不同的 A 和 B,无论它们如何相关,Foo<A>都不兼容。Foo<B>

我认为这是 API 中的设计错误。的返回类型entrySet()应该是Set<? extends Map.Entry<K,V>>. 原因如下:如果您阅读 的文档entrySet()它说可以从 Set 中读取内容,可以从 Set 中删除内容(这会导致底层映射发生更改),但无法将内容添加到 Set 中。这完全符合 Producer 的角色——您无需向其添加任何内容。根据 PECS 规则,extends应使用 -wildcard 集合类型。

于 2013-07-17T09:30:03.070 回答
1

除非您需要MapTreeNode方法中的特定内容,否则将其视为 a Map.Entry,意思是声明child_set

private Set<Map.Entry<K,V>> child_set = null;

由于MapTreeNodeextends Map.Entry,你应该没问题。

于 2013-07-15T20:25:09.187 回答
1

这是迄今为止我在避免代码重复方面所能做的最好的事情:

private abstract class AbstractChildSet<T extends Map.Entry<K,V>> extends AbstractSet<T> {
    @Override
    public boolean remove(Object o) {
        if (o == null || !(o instanceof Map.Entry)) {
            return false;
        }

        MapTreeNode<K,V> node;
        if (o instanceof MapTreeNode)
            node = (MapTreeNode<K,V>) o;
        else
            node = MapTreeNode.this.child(((Map.Entry<K,V>) o).getKey());

        if (node == null || !isParentOf(node))
            return false;

        node.removeFromParent();
        return true;
    }

    @Override
    public int size() {
        return MapTreeNode.this.childCount();
    }

    @Override
    public void clear() {
        MapTreeNode.this.removeAllChildren();
    }
}

private final class ChildSet extends AbstractChildSet<MapTreeNode<K,V>> {
    @Override       
    public boolean add(MapTreeNode<K,V> node) {
        if (MapTreeNode.this.containsKey(node.getKey()))
            return false;

        MapTreeNode.this.addChild(node);
        return true;
    }

    @Override
    public Iterator<MapTreeNode<K,V>> iterator() {
        return children.values().iterator();
    }
}

private final class EntrySet extends AbstractChildSet<Map.Entry<K,V>> {
    @Override
    public boolean add(Map.Entry<K,V> entry) {
        if (MapTreeNode.this.containsKey(entry.getKey()))
            return false;

        MapTreeNode new_child = new HashMapTreeNode(MapTreeNode.this, entry.getKey(), entry.getValue());

        MapTreeNode.this.addChild(new_child);
        return true;
    }

    @Override
    public Iterator<Map.Entry<K,V>> iterator() {
        return new EntryIterator();
    }
}
于 2013-07-17T16:20:20.250 回答
0

据我所知,您已经实现了新类来获取条目类型的孩子。要再次构建地图,我将遍历一组条目并重建地图。

不确定我是否像其他人一样在这里提供了帮助,我无法完全阅读该课程还包含的其他内容。

于 2013-07-15T20:55:57.700 回答
-1

你为什么不简单地像这样投射它:

public Map<K,V> asMap() {
    return (Map<K,V>) this;
}
于 2013-07-15T23:07:52.603 回答