简体   繁体   English

在每个级别具有多个子(已排序)的Java树结构

[英]Java tree structure with multiple children (sorted) at each level

I'm working with a flat List of objects, which nevertheless are associated with each other in parent-child relationships. 我正在使用一个平面的对象列表,但它们在父子关系中相互关联。 An object may have any number of children, or none at all. 一个对象可以有任意数量的子节点,或者根本没有。 I need to display these objects as a tree, showing those relationships. 我需要将这些对象显示为树,显示这些关系。 Each level of the tree should be sorted (the objects are compatible with Collections.sort() ). 树的每个级别应进行分类(对象兼容Collections.sort()

The question is two-part: 问题分为两个部分:

  1. Does Java have a good out-of-the-box data structure for holding such a tree, or do I need to write one from scratch? Java是否有一个很好的开箱即用的数据结构来保存这样一棵树,或者我是否需要从头开始编写一个? (not a huge task, but there's no sense in reinventing the wheel) I know about DefaultTreeModel in Swing... but this application is running on the server-side, and use of the Swing package will get frowned upon in code review. (这不是一项艰巨的任务,但重新发明轮子没有任何意义)我知道Swing中的DefaultTreeModel ...但是这个应用程序在服务器端运行,并且在代码审查中使用Swing包将不受欢迎。

  2. What would be the best pattern for loading a flat List into such a data-structure? 将平面列表加载到这样的数据结构中的最佳模式是什么? My first thought is to identify the root-level objects, and then use a recursive method to traverse down through their children, grandchildren, etc. However, for the requirement of sorting the peers at each level in the tree... I'm not sure if it makes more sense to worry about this when I'm building the tree, or worry about it later when I'm parsing the tree for display. 我的第一个想法是识别根级对象,然后使用递归方法遍历他们的子孙,孙子等等。但是,为了在树中的每个级别对对等体进行排序的要求...我是当我正在构建树时,不确定是否更有意义地担心这一点,或者在我解析树以供显示时担心它。

Here is a quick-and-dirty Tree implementation that uses TreeSets on all levels (you can supply a comparator, or natural ordering will be used): 这是一个快速而又脏的Tree实现,它在所有级别上使用TreeSet(您可以提供比较器,或者使用自然顺序):

public class Tree<T> {

    private final Node<T> rootElement;

    public void visitNodes(final NodeVisitor<T> visitor){
        doVisit(rootElement, visitor);
    }

    private static <T> boolean doVisit(final Node<T> node,
        final NodeVisitor<T> visitor){
        boolean result = visitor.visit(node);
        if(result){
            for(final Node<T> subNode : node.children){
                if(!doVisit(subNode, visitor)){
                    result = false;
                    break;
                }
            }
        }
        return result;
    }

    public interface NodeVisitor<T> {

        boolean visit(Node<T> node);
    }

    public Node<T> getRootElement(){
        return rootElement;
    }

    private static final class NodeComparator<T> implements Comparator<Node<T>>{

        private final Comparator<T> wrapped;

        @Override
        public int compare(final Node<T> o1, final Node<T> o2){
            return wrapped.compare(o1.value, o2.value);
        }

        public NodeComparator(final Comparator<T> wrappedComparator){
            this.wrapped = wrappedComparator;
        }

    }

    public static class Node<T> {

        private final SortedSet<Node<T>> children;

        private final Node<T> parent;

        private T value;

        private final Comparator<?> comparator;

        @SuppressWarnings("unchecked")
        Node(final T value, final Node<T> parent, final Comparator<?> comparator){
            this.value = value;
            this.parent = parent;
            this.comparator = comparator;
            children =
                new TreeSet<Node<T>>(new NodeComparator<T>((Comparator<T>) comparator));
        }

        public List<Node<T>> getChildren(){
            return new ArrayList<Node<T>>(children);
        }

        public Node<T> getParent(){
            return parent;
        }

        public T getValue(){
            return value;
        }

        public void setValue(final T value){
            this.value = value;
        }

        public Node<T> addChild(final T value){
            final Node<T> node = new Node<T>(value, this, comparator);
            return children.add(node) ? node : null;
        }

    }

    @SuppressWarnings("rawtypes")
    private static final Comparator NATURAL_ORDER = new Comparator(){

        @SuppressWarnings("unchecked")
        @Override
        public int compare(final Object o1, final Object o2){
            return ((Comparable) o1).compareTo(o2);
        }
    };

    private final Comparator<?> comparator;

    public Tree(){
        this(null, null);
    }

    public Tree(final Comparator<? super T> comparator){
        this(comparator, null);
    }

    public Tree(final Comparator<? super T> comparator, final T rootValue){
        this.comparator = comparator == null ? NATURAL_ORDER : comparator;
        this.rootElement = new Node<T>(rootValue, null, this.comparator);
    }

    public Tree(final T rootValue){
        this(null, rootValue);
    }

}

Here is some sample code against it: 以下是一些针对它的示例代码:

final Tree<Integer> tree = new Tree<Integer>();
final Node<Integer> rootNode = tree.getRootElement();
rootNode.setValue(1);
final Node<Integer> childNode = rootNode.addChild(2);
final Node<Integer> newChildNode = rootNode.addChild(3);
newChildNode.addChild(4);
tree.visitNodes(new NodeVisitor<Integer>(){

    @Override
    public boolean visit(final Node<Integer> node){
        final StringBuilder sb = new StringBuilder();
        Node<Integer> curr = node;
        do{
            if(sb.length() > 0){
                sb.insert(0, " > ");
            }
            sb.insert(0, String.valueOf(curr.getValue()));
            curr = curr.getParent();
        } while(curr != null);
        System.out.println(sb);
        return true;
    }
});

Output: 输出:

1 1
1 > 2 1> 2
1 > 3 1> 3
1 > 3 > 4 1> 3> 4

What would be the best pattern for loading a flat List into such a data-structure? 将平面列表加载到这样的数据结构中的最佳模式是什么? My first thought is to identify the root-level objects, and then use a recursive method to traverse down through their children, grandchildren, etc. 我的第一个想法是识别根级对象,然后使用递归方法遍历他们的子孙,孙子等。

If I understand correctly, you only have a flat list, without any concrete associations between its elements, and you can detect somehow whether a particular element is the child of another. 如果我理解正确,你只有一个单独的列表,它的元素之间没有任何具体的关联,你可以以某种方式检测某个特定元素是否是另一个元素的子元素。

In this case, you could 在这种情况下,你可以

  1. sort the list 对列表进行排序
  2. (identify the root node, if it is not known yet) (标识根节点,如果还不知道的话)
  3. put the root into a queue 将根放入队列中
  4. take the first node from the queue 从队列中取出第一个节点
  5. starting from the first element of the list, check each element whether it is a child of the current node; 从列表的第一个元素开始,检查每个元素是否是当前节点的子元素; if so, add it to the current level of the tree and put it into the queue 如果是这样,将其添加到树的当前级别并将其放入队列中
  6. repeat from step 4. 从第4步开始重复。

If detecting parent-child relationship is costly, you could improve performance by storing a flag for / nulling out each node whose location within the tree is already identified, so that you can jump over them when traversing the list. 如果检测到父子关系成本很高,则可以通过为已经标识了树中位置的每个节点存储/ null的标志来提高性能,以便在遍历列表时跳过它们。 Alternatively, you may copy the whole sorted list into a linked list so that it is trivial to remove processed elements from it. 或者,您可以将整个排序列表复制到链接列表中,以便从中删除已处理的元素。

There are no tree structures in Java, but there are sorted ones: TreeSet and TreeMap. Java中没有树结构,但有一些排序:TreeSet和TreeMap。 See for some hints java data-structure to simulate a data tree 请参阅一些提示java数据结构来模拟数据树

The approach you came up with is what I would do. 你提出的方法是我会做的。

How to go about building the tree really depends on what information you have in the initial List. 如何构建树实际上取决于您在初始列表中的信息。

  • If each node contains a reference to its parent and a collection of its children, you don't need to build anything other than the root set. 如果每个节点都包含对其父节点及其子集合的引用,则不需要构建除根集合之外的任何其他节点。

  • If each node only has a reference to its parent, you do need to build a tree; 如果每个节点只有对其父节点的引用,则需要构建一个树; but you can do it in a single pass over the data using a HashMap to map each node to a list (which you build) of its children. 但您可以使用HashMap在数据上单次传递,将每个节点映射到其子节点的列表(您构建)。

  • If the nodes don't even contain a reference to their parents, you'll have to do what Péter suggests. 如果节点甚至不包含对父母的引用,那么你必须做Péter所建议的。

In any case, I wouldn't bother sorting the whole List first. 无论如何,我不会先打扰整个List。 Sorting a large List will be slower than sorting lots of little ones with the same total length. 对大型列表进行排序比对具有相同总长度的大量小型列表进行排序要慢。 (This follows from sorting being O(n log n).) (这是因为排序为O(n log n)。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM