使用不相等的键提取器和分类器函数对列表进行分组



我想将元素分组到提交列表(类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())));

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

所以问题如下:

如何从commit -> commit.getComitter().getUsername()上的List<LegacyCommit>分组获取Map<LegacyUser, List<LegacyCommit>>

我假设用户名是一个String(尽管任何Comparable都可以使用此解决方案(。然后,您可以按提交分组到按提交者的用户名排序的排序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. 收集Map<String, List<LegacyCommit>> commitsByUsername
  2. 创建空Map<LegacyUser, List<LegacyCommit>> results
  3. 循环遍历 commitsByUsername 中的列表,对于每个项目,从列表中获取第一个提交,将其用作键并将列表本身添加到results

起初,我使用一堆流和收集器尝试了许多不同的方法,但总是以编译器错误告终。感觉有时最简单的解决方案是最好的解决方案。

我不相信你现在能够使用典型的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);
}

这应该可以做到。 首先按用户名分组,然后使用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));

最新更新