使用不等密钥提取器和分类器函数对List进行分组

时间:2014-08-28 14:56:28

标签: java java-8 java-stream

我想在提交列表(类LegacyCommit)中对元素进行分组,以便所有对同一用户的提交都属于自己的映射。

以下是首先获取nonDistinctCommits

的代码
Map<Boolean, List<LegacyCommit>> partitionedCommits = pushEvent.getCommits().stream()
    .collect(Collectors.partitioningBy(LegacyCommit::isDistinct));

List<LegacyCommit> distinctCommits = partitionedCommits.get(true);
List<LegacyCommit> nonDistinctCommits = partitionedCommits.get(false);

现在我希望通过Map<LegacyUser, List<LegacyCommit>>分组获得commit -> commit.getCommitter().getUsername(),但我遇到两种情况都不起作用:

情况1

Map<LegacyUser, List<LegacyCommit>> committerGroupedNonDistinctCommits = nonDistinctCommits.stream()
    .collect(Collectors.groupingBy(LegacyCommit::getCommitter));

这是关闭的,但是LegacyCommiter::getCommitter上的地图,没有定义equals()方法,也不想通过这种方式进行。

所以我最终使用了......

情况2

我想对用户名进行分组,同时将LegacyUser作为键存储在地图中,我试图这样做:

Map<LegacyUser, List<LegacyCommit>> committerGroupedNonDistinctCommits = nonDistinctCommits.stream()
    .collect(Collectors.groupingBy(commit -> commit.getCommitter().getUsername(), Collectors.mapping(LegacyCommit::getCommitter, Collectors.toList())));

但它无法编译,因为类型参数是错误的。

所以问题如下:

如何从Map<LegacyUser, List<LegacyCommit>>的{​​{1}}分组中获取List<LegacyCommit>

3 个答案:

答案 0 :(得分:3)

我认为用户名String(尽管任何Comparable都可以使用此解决方案)。然后,您可以将 committer 分组到已排序的Map中,该Map<LegacyUser, List<LegacyCommit>> committerGroupedNonDistinctCommits = nonDistinctCommits.stream().collect(Collectors.groupingBy( LegacyCommit::getCommitter, ()->new TreeMap<>(Comparator.comparing(LegacyUser::getUserName)), Collectors.toList())); 提交者用户名排序:

Comparator

有序地图会将密钥视为相等,根据Collector相等, Map将使用提供的LegacyUser的映射。因此,equals没有{{1}}方法时无关紧要。

答案 1 :(得分:2)

解决此问题的一种方法是使用三步解决方案。

  1. 收集Map<String, List<LegacyCommit>> commitsByUsername
  2. 创建一个空的Map<LegacyUser, List<LegacyCommit>> results
  3. 循环浏览commitsByUsername中的列表,对于每个项目,从列表中获取第一个提交,将其用作关键字并将列表本身添加到results
  4. 我最初使用一堆流和收集器尝试了许多不同的方法,但最终总是出现编译器错误。感觉有时最简单的解决方案是最好的解决方案。

    我不相信你现在可以使用典型的Java 8单行程来做到这一点。

    这是一个SSCCE:

    static class User {
        private static int COUNTER = 0;
        String username;
        int notSame = COUNTER++;
    
        @Override
        public String toString() {
            return String.format("{%s: %d}", username, notSame);
        }
    
    }
    static class LegacyCommit {
        User user;
        String value;
    
        public static LegacyCommit create(String user, String value) {
            LegacyCommit commit = new LegacyCommit();
            commit.user = new User();
            commit.user.username = user;
            commit.value = value;
            return commit;
        }
    
        @Override
        public String toString() {
            return String.format("%s by %s", value, user);
        }
    }
    
    public static void main(String[] args) {
    
        List<LegacyCommit> commits = new ArrayList<>();
        commits.add(LegacyCommit.create("a", "v1"));
        commits.add(LegacyCommit.create("b", "v2"));
        commits.add(LegacyCommit.create("c", "v3"));
        commits.add(LegacyCommit.create("a", "v4"));
        commits.add(LegacyCommit.create("b", "v5"));
        commits.add(LegacyCommit.create("c", "v6"));
        commits.add(LegacyCommit.create("a", "v7"));
    
    
        Map<String, List<LegacyCommit>> commitsByUsername = commits.stream().collect(groupingBy(commit -> commit.user.username));
        Map<User, List<LegacyCommit>> resultsss = new HashMap<>();
        commitsByUsername.values().forEach(list -> resultsss.put(list.get(0).user, list));
        System.out.println(resultsss);
    }
    

答案 2 :(得分:2)

这应该这样做。首先按用户名分组,然后使用getCommiter()作为密钥重新制作地图。

Map<LegacyUser, List<LegacyCommit>> committerGroupedNonDistinctCommits = 
    nonDistinctCommits.stream()
        .collect(groupingBy(c -> c.getCommiter().getUsername()))
        .values().stream()
        .collect(toMap(lst -> lst.get(0).getCommiter(), lst->lst));