代码之家  ›  专栏  ›  技术社区  ›  jonesy

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

  •  2
  • jonesy  · 技术社区  · 6 年前

    我正试图将一些实体组织起来,把孩子放在家长的身上。

    @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));
    

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

    1 回复  |  直到 6 年前
        1
  •  2
  •   Eugene    6 年前

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

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

    比我定义的小计算 Map (s)这将有助于我快速搜索:

        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())));
    

    第一个应该是明显的,它包含一个ID和一个 JournalEntry .

    第二个保持 parentId 到一组ID。基本上:

    -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);
            });
        }
    }
    

    我已经测试了一个相当简单的案例 == )对我来说似乎还不错。