java - 如何使用 java 8 对父子实体进行分组

标签 java java-8

我正在尝试对一些实体进行分组,将子级放在父级上..

@Entity 
public class JournalEntry {
    Integer id;
    String message;
    Integer parentId;        
}

数据传输对象看起来像这样......

public class JournalDTO {
    public JournalDTO (Integer id, String message, List<JournalDTO> childEntries) {
        this.id = id;
        this.message = message;
        this.childEntries = childEntries;
    }
    Integer id;
    String message;
    List<JournalDTO> childEntries;
}

所以我希望最终得到一个列表,它没有父ID,因此是顶级的,然后它们将有一组子实体,并且在这些子实体中它们也可能有子实体。有没有办法做到这一点。 我的想法是首先获取所有顶级条目,如下所示..

List<JournalEntry> journalEntries = service.fetchJournalEntries();
List<JournalEntry> topLevel = journalEntries.stream().filter(e -> null==e.getParentId()).collect(toList());
journalEntries.removeAll(topLevel);
List<JournalDTO> journalDTOs = topLevel.stream()
                            .map(tl -> new JournalDTO(tl.getId(), tl.getMessage(), new ArrayList<JournalDTO>()))
                            .collect(toList());

然后我按父 ID 对剩余条目进行分组..

Map<Integer, List<JournalEntry>> childMap = journalEntries.stream().collect(groupingBy(Integer::getParentId));

然后我可以迭代这个映射并将 childEntities 添加到父实体,但这只会给我第二个级别,然后我必须确保没有子级的子级等...有没有更好的方法这样做吗?

最佳答案

多么有趣的问题啊。首先,为了简单起见,我定义了一个方法:

private static JournalDTO toDTO(JournalEntry entry) {
    return new JournalDTO(entry.getId(), entry.getMessage(), new ArrayList<>());
}

然后我定义了一些小型计算Map来帮助我快速搜索:

    Map<Integer, JournalEntry> identity = entries.stream()
            .collect(Collectors.toMap(JournalEntry::getId, Function.identity()));

    Map<Integer, Set<Integer>> map = entries.stream()
            .collect(Collectors.groupingBy(
                    x -> x.getParentId() == null ? -1 : x.getParentId(),
                    Collectors.mapping(JournalEntry::getId, Collectors.toSet())));

第一个应该很明显,它包含一个与 JournalEntry 配对的 ID。

第二个保存了一组 id 的 parentId。基本上:

-1 == 1 // -1 meaning it has no parents
 1 == 2 // 1 has a child with id 2
 2 == 3, 4 // 2 has two children with id 3 and 4
 4 == 5, 6 // ... 

如果您考虑一下 - 例如,这就是我找到整个“家庭”的方式(如果此处需要更多详细信息,请告诉我)。

剩下的就是带有递归方法的简单代码:

// get those that have no parents first
Set<Integer> ids = map.get(-1);

// this is the ultimate result 
List<JournalDTO> all = new ArrayList<>();

// for each entity with no parents, start searching in the map
ids.forEach(x -> {
     JournalDTO parentDTO = toDTO(identity.get(x));
     recursive(x, map, identity, parentDTO);
     all.add(parentDTO);
}); 

当然还有最重要的部分:

private static void recursive(
        Integer parentId,
        Map<Integer, Set<Integer>> map,
        Map<Integer, JournalEntry> identity,
        JournalDTO journalDTO) {

    Set<Integer> childrenIds = map.get(parentId);

    if (childrenIds != null && !childrenIds.isEmpty()) {
        childrenIds.forEach(x -> {
            JournalDTO childDTO = toDTO(identity.get(x));
            journalDTO.getChildEntries().add(childDTO);
            recursive(x, map, identity, childDTO);
        });
    }
}

我已经在一个相当简单的情况下对此进行了测试(带有 == 的情况),并且似乎对我来说工作得很好。

关于java - 如何使用 java 8 对父子实体进行分组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52623913/

相关文章:

java - 如何使 ListView 中的行适应其在 Android 应用程序中的内容

java - 每次执行的 AES 加密 key 生成是否一致?

java - 单击按钮后如何将Paypal集成到付款用户?

Java:JComboBox addItem函数与jdk8和jdk7不同

java - 计算列表中对象的出现次数

java - 确保用户在 Java Web App 中经过身份验证

java - CompletableFuture 从内部注入(inject)

java - 从列表中的任何值获取键

java - 在List中输入几行

java - 将日期转换为 LocalDate 在公元 200 年左右返回奇怪的结果