我可以展平多深度自引用实体吗?

时间:2016-11-10 13:00:45

标签: java list java-stream flatten self-reference

我有一个实体自我引用。

public class Category {

    private List<Category> children;
}

通过给定的类别列表,我怎样才能将它们全部展平?

C11
    C21
        C31
        C32
    C22
C12

我想要一份

列表
C1 C21 C31 C32 C22 C12

我试过了。

public static <T extends BaseEntity & SelfReferencing<T>> void flatten(
        final T parent, final Function<T, Stream<T>> function,
        final Consumer<T> consumer) {
    function.apply(parent).forEach(child -> {
        consumer.accept(child);
        flatten(child, function, consumer);
    });
}

3 个答案:

答案 0 :(得分:2)

我拿了你的代码并稍微改进了一下。

public static <T extends BaseEntity & SelfReferencing<T>> List<T> flatten(
        final T parent, final Function<T, Stream<T>> function) {
    List<T> res = new ArrayList<>();
    res.add(parent);
    res.addAll(function.apply(parent).flatMap(child -> {
        flatten(child, function).stream();
    }).collect(Collectors.toList()));
    return result;
}

我不明白为什么你需要consumer我删除了它。还有一些转换为流和返回。

答案 1 :(得分:1)

你是否在寻找像下面的“扁平”一样的递归函数?...

import java.util.ArrayList;
import java.util.List;

public class Flatten {

    static class Category {
        private List<Category> children;
        private String name;

        public Category(String name) {
            this.name = name;
        }

        public List<Category> getChildren() {
            if(children == null) {
                children = new ArrayList<>();
            }
            return children;
        }

        public void setChildren(List<Category> children) {
            this.children = children;
        }

        @Override
        public String toString() {
            return name;
        }
    }

    public static void main(String[] args) {
        Category root = new Category("ROOT");
        Category c11 = new Category("C11");
        Category c12 = new Category("C12");
        Category c21 = new Category("C21");
        Category c22 = new Category("C22");
        Category c31 = new Category("C31");
        Category c32 = new Category("C32");

        root.getChildren().add(c11);
        root.getChildren().add(c12);
        c11.getChildren().add(c21);
        c11.getChildren().add(c22);
        c21.getChildren().add(c31);
        c21.getChildren().add(c32);

        List<Category> flat = flatten(root.getChildren());

        System.out.println(flat);
    }

    private static List<Category> flatten(List<Category> c) {
        List<Category> flatList = new ArrayList<>();

        flatten(flatList, c);

        return flatList;
    }

    private static void flatten(List<Category> flatList, List<Category> c) {
        for (Category category : c) {
            flatList.add(category);
            flatten(flatList, category.getChildren());
        }
    }
}

如果层次结构中存在“循环”(例如,C31将C11作为子项),则可以添加以下内容以确保在内存不足之前不会永远循环...

private static void flatten(List<Category> flatList, List<Category> c) {
    for (Category category : c) {
        if(!flatList.contains(category)) {
            flatList.add(category);
            flatten(flatList, category.getChildren());
        }
    }
}

现在,如果你添加......

c21.getChildren().add(c11);

对于“主要”方法,它应该不会爆炸。

希望这有帮助。

答案 2 :(得分:1)

如果您正在寻找基于流的解决方案,则可以创建以下方法

public static <T> Stream<T> flatten(T node, Function<T,Stream<T>> children) {
    return Stream.concat(Stream.of(node),
        children.apply(node).flatMap(ch->flatten(ch, children)));
}

并重新实施“为每个方法执行操作”,例如

public static <T> void flatten(
    T parent, Function<T, Stream<T>> function, Consumer<T> consumer) {

    flatten(parent, function).forEach(consumer);
}

但是既然你说,你想要一个List,你也可以使用像

这样的流媒体方法
public static <T> List<T> toFlatList(T parent, Function<T, Stream<T>> function) {
    return flatten(parent, function).collect(Collectors.toList());
}

可以使用Category草图

List<Category> list=toFlatList(category, c -> c.getChildren().stream());