【问题标题】:Java Stream Map List to Set and preserve Order [duplicate]Java Stream Map List 设置和保留顺序[重复]
【发布时间】:2018-08-10 16:26:59
【问题描述】:

我有一个 Answer 类和一个 User 类。

答案有一个getUser(),用户有一个getPoints()

从答案列表中,我想获得按点排序的用户 HashSet。我尝试了以下操作:

 Set<User> collectSet = list.stream().map(Answer::getUser)
            .sorted(Comparator.comparing(User::getPoints))
            .collect(Collectors.toCollection(HashSet::new));

 collectSet.forEach(a -> System.out.println(a.toString()));

不幸的是,这似乎并没有保留顺序。输出总是不同的。

有趣的是,与 list 相同的示例确实可以正常工作

List<User> collectList = list.stream().map(Answer::getUser)
            .sorted(Comparator.comparing(User::getPoints))
            .collect(Collectors.toList());

collectList.forEach(a -> System.out.println(a.toString()));

我做错了什么?

【问题讨论】:

  • 一个 HashSet 没有任何顺序。如果要保留插入顺序,请使用 List 或 LinkedHashSet
  • 我不想要任何重复的元素。这就是为什么我需要一套。
  • 然后使用.distinct()
  • 你认为 LinkedHashSet 是什么?为什么不阅读 javadoc?
  • forEach 不是确定性的。至少使用forEachOrdered

标签: java java-stream


【解决方案1】:
 Collectors.toCollection(LinkedHashSet::new)

首先,您根据一些 Comparator 对这些条目进行排序 - 给它们“一些”顺序,然后将它们放入 HashSet (顺序已损坏),因此结果不同。使用LinkedHashSet 收集它们以保持排序顺序。

如果您仍想将它们收集到List,您可以:

 yourList.stream()
         .sorted(....)
         .distinct() 
         .collect(Collectors.toList())

操作本身的顺序(distinct vs sortedmatters

【讨论】:

  • assuming hashcode/equals implemented 也应该在仅使用普通的旧 Set 时假定
  • @Lino :) 对,将编辑
  • distinctsorted 的顺序只对自然顺序有意义,它应该与相等性兼容。对于自定义比较器,例如 OP 的仅比较“points”属性的比较器,该顺序不能用于优化distinct 操作。
  • @Holger 哦!我想我从来不明白...谢谢!
  • 想一想,也可以对此进行优化,但与目前用于自然顺序的算法不同。
【解决方案2】:

HashSet 不保留插入顺序,但 List 保留。你可以试试LinkedHashSet

编辑: 另一种选择是使用TreeSet。这是一个集合,因此删除了重复的值。并且元素在插入时进行排序。使用方法compareTo(由Comparable 接口提供)进行排序。 例如:

// Answer class
public class Answer {

    private final User user;

    public Answer(final User user) {
        this.user = user;
    }

    public User getUser() {
        return user;
    }
}

// User class
public class User implements Comparable<User> {

    private final int points;

    public User(final int points) {
        this.points = points;
    }

    public int getPoints() {
        return points;
    }

    @Override
    public int compareTo(User other) {
        // Sort user by their points (ascending order)
        return points - other.points;
    }

    @Override
    public String toString() {
        return "User{" + "points=" + points + '}';
    }
}

然后在你的主代码中:

// Main
List<Answer> answers = Arrays.asList(
    new Answer(new User(0)),
    new Answer(new User(20)),
    new Answer(new User(1)),
    new Answer(new User(20)),
    new Answer(new User(10))
);

answers
    .stream()
    .map(Answer::getUser)
    .collect(Collectors.toCollection(TreeSet::new))
    .forEach(System.out::println);

输出:

User{points=0}
User{points=1}
User{points=10}
User{points=20}

【讨论】:

  • 此代码仅在用户仅包含 points 属性时才有效。在现实生活场景中,您肯定不想认为所有用户的积分值相同,这就是TreeSet 会发生的情况。
【解决方案3】:

可能你想使用TreeSet

两个用户号相同。点数不相等,所以应该有一些uniqueId 供用户使用。

我认为它是userId

这里不需要hashCodeequals(但很好,请参阅documentation)因为TreeSet使用Comparator(或者User类可以实现Comparable)来确定两个元素是否是否相等。

TreeSet<User> users = answers.stream()
    .map(Answer::getUser)
    .collect(Collectors.toCollection(() -> new TreeSet<User>(
       Comparator.comparingInt(User::getPoints).thenComparing(User::getUserId))));

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-08-23
    • 1970-01-01
    • 1970-01-01
    • 2021-02-23
    • 1970-01-01
    • 2012-05-31
    相关资源
    最近更新 更多