从平面列表构建分层列表



我有列表,其中包含由相同类型(类(表示的所有父项和子项。我想从平面列表生成分层列表。

下面是我的班级,代表父母和孩子。在这里,我需要根据parentId为父级映射子项。根级别(无父级(以父 ID 0 开头。

import java.util.ArrayList;
import java.util.List;
class Customers {
    private int id;
    private String name;
    private int parentId;
    private List<Customers> childs;
    public Customers(int id, String name, int parentId) {
        this(id, name, parentId, null);
    }
    public Customers(int id, String name, int parentId, List<Customers> childs) {
        this.id = id;
        this.name = name;
        this.parentId = parentId;
        this.childs = childs;
    }
}
public class Application {
    public static void main(String[] args) {
        //Input List
        Customers customer = new Customers(1, "Hel", 0);
        Customers customerChild1 = new Customers(2, "Hel-Child1", 1);
        Customers customerChild2 = new Customers(5, "Hel-Child2", 1);
        Customers customerInnerChild1 = new Customers(3, "Hel-InnerChild1", 2);
        Customers customerInnerChild2 = new Customers(4, "Hel-InnerChild2", 2);
        List<Customers> customers = new ArrayList();
        customers.add(customerInnerChild1);
        customers.add(customerInnerChild2);
        customers.add(customer);
        customers.add(customerChild1);
        customers.add(customerChild2);
    }
    public static List<Customers> hierarchicalList(List<Customers> Customers) {
        return null;
    }
}

我将List<Customer>作为输入传递给某个方法。该方法应返回具有父子关系的List<Customer>

我需要帮助以优化的方式建立父母和孩子的关系。

输入:

[
  {
    "id": 1,
    "name": "Hel",
    "parentId": 0
  },
  {
    "id": 5,
    "name": "Hel-Child2",
    "parentId": 1
  },
  {
    "id": 4,
    "name": "Hel-InnerChild2",
    "parentId": 2
  },
  {
    "id": 3,
    "name": "Hel-InnerChild1",
    "parentId": 2
  },
  {
    "id": 2,
    "name": "Hel-Child1",
    "parentId": 1
  }
]

预期输出:

[
  {
    "id": 1,
    "name": "Hel",
    "parentId": 0,
    "childs": [
      {
        "id": 2,
        "name": "Hel-Child1",
        "parentId": 1,
        "childs": [
          {
            "id": 3,
            "name": "Hel-InnerChild1",
            "parentId": 2
          }, {
            "id": 4,
            "name": "Hel-InnerChild2",
            "parentId": 2
          }
        ]
      }, {
        "id": 5,
        "name": "Hel-Child2",
        "parentId": 1
      }
    ]
  }
]

注意:输入列表未排序。

如果您只需要检查谁是每个孩子的父级,则只需过滤列表并为每个元素设置子元素即可。像这样:

public List<Customer> getHierarchicalList(final List<Customer> originalList) {
    final List<Customer> copyList = new ArrayList<>(originalList);
    copyList.forEach(element -> {
        originalList
                .stream()
                .filter(parent -> parent.id == element.parentId)
                .findAny()
                .ifPresent(parent -> {
                    if (parent.childs == null) {
                        parent.childs = new ArrayList<>();
                    }
                    parent.childs.add(element);
                    originalList.remove(element);
                });
    });
    return originalList;
}
我想

扩展Rhuan的答案,

通过使用 Rhuan 的代码,您将获得所有节点都铺设在根级别,并且只有一个节点具有正确的内容和顺序,通过删除错误的内容,您将获得更好的结果:

public List<Customer> getHierarchicalList(final List<Customer> originalList) {
    final List<Customer> copyList = new ArrayList<>(originalList);
    copyList.forEach(element -> {
        originalList
                .stream()
                .filter(parent -> parent.id == element.parentId)
                .findAny()
                .ifPresent(parent -> {
                    if (parent.childs == null) {
                        parent.childs = new ArrayList<>();
                    }
                    parent.childs.add(element);
                    /* originalList.remove(element); don't remove the content before completing the recursive */
                });
    });
    originalList.subList(1, originalList.size()).clear();
    return originalList;
}

注意子列表清除。

我也想扩展Rhuan的答案。

如果你的代码有无限循环,你需要比较父和元素 id,它们不应该等于parent.id != element.id

所以你的代码将是:

public List<Customer> getHierarchicalList(final List<Customer> originalList) {
    final List<Customer> copyList = new ArrayList<>(originalList);
    copyList.forEach(element -> {
        originalList
                .stream()
                .filter(parent -> parent.id == element.parentId && parent.id != element.id)
                .findAny()
                .ifPresent(parent -> {
                    if (parent.childs == null) {
                        parent.childs = new ArrayList<>();
                    }
                    parent.childs.add(element);
                    originalList.remove(element);
                });
    });
    return originalList;
}

我最近写了这个Collector正是为了这个目的:

public static final <K, E, R> Collector<R, ?, List<E>> intoHierarchy(
    Function<? super R, ? extends K> keyMapper,
    Function<? super R, ? extends K> parentKeyMapper,
    BiFunction<? super R, ? super List<E>, ? extends E> recordMapper
) {
    record Tuple3<T1, T2, T3>(T1 t1, T2 t2, T3 t3) {}
    return Collectors.collectingAndThen(
        Collectors.toMap(keyMapper, r -> {
            List<E> e = new ArrayList<>();
            return new Tuple3<R, C, E>(r, e, recordMapper.apply(r, e));
        }),
        m -> {
            List<E> r = new ArrayList<>();
 
            m.forEach((k, v) -> {
                K parent = parentKeyMapper.apply(v.t1());
                E child = v.t3();
 
                if (m.containsKey(parent))
                    m.get(parent).t2().add(child);
                else
                    r.add(child);
            });
 
            return r;
        }
    );
}

现在,您可以按如下方式映射层次结构:

List<Customers> hierarchy = customers
    .stream()
    .collect(intoHierarchy(
        c -> c.id,
        c -> c.parentId,
        (c, l) -> new Customer(c.id, c.name, c.parentId, l)
    ));

其他解决方案似乎O(N^2).这是O(N)

这个类已经是分层的了。

如果打印父项,

然后递归打印子项,则会看到一棵树。

你期待会发生什么?

这只是一个问题,你想如何迭代树:深度第一还是广度第一?

您关于从平面列表

转到树的问题是关于解析平面列表。 您如何区分平面列表中的父母和孩子? 这应该会给你一个关于如何继续的提示。

您需要一个工厂类,该类采用客户列表,分析列表,并在适当的树结构中返回客户的根实例及其子实例。

最新更新