Java 8 Stream - .max() with duplicates

26

所以我有一组对象,它们有一个步骤变量,可以是1-4。

public class MyClass {
    private Long step;

    //other variables, getters, setters, etc.
}

Collection<MyClass> myOjbects = /*获取集合*/;

然后我想从集合中获取一个具有最大步骤值的MyClass实例,所以我执行以下操作:

final Optional<MyClass> objectWithMaxStep =
   myObjects.stream().max(Comparator.comparing(MyClass::getStep));

然而,有些情况下,集合中会有多个步长等于4的MyClass实例。

我的问题是,在Optional中返回哪个实例,或者当流中有多个对象具有正在比较的最大值时,它是否会抛出异常?

Java 8文档并没有指定在这种情况下会发生什么。


1
如果它们相等,为什么顺序很重要呢? - the8472
2
对于我使用的特定示例,这并不重要,因为我只是从返回的对象中提取步骤。但是,类中还有其他变量,因此处于相同步骤并不意味着对象相等。因此,在其他情况下,返回哪个对象可能很重要。我提出问题的原因主要是为了了解当多个对象在比较字段上具有相同值时max()函数如何工作。 - Andrew Mairose
2
为什么不使用链接比较器(chained comparator),包括那些其他变量,如果它们很重要的话? - the8472
如果多个变量有影响的话,我会使用自定义比较器。但在这种情况下它们并不重要。我只是对max()函数如何确定返回哪个对象感兴趣。我的代码运行良好。我不是在解决问题,而是想理解发生了什么。 - Andrew Mairose
2个回答

22

max 是通过使用 maxBy 函数来实现的,该函数可对集合进行迭代以找到最大值:

 public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
        Objects.requireNonNull(comparator);
        return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
    }

在这里,你可以看到当两个元素相等时,即compare返回0时,将返回第一个元素。因此,在你的情况下,将返回具有最高stepMyClass对象集合中的第一个

更新:正如用户the8472在评论中正确提到的那样,你不应该依赖于没有在Javadocs中明确指定的实现。但是,你可以编写max方法的单元测试,以了解它是否在标准java库中发生了逻辑变化。


22
只要Java文档没有规定行为,你就不应该太依赖于实现。 - the8472

10

由于文档未指定排序顺序,因此在JVM的不同版本或不同实现中,排序顺序可能会有所不同。

如果您关心返回哪个元素,请编写自己的约简操作或收集器以按照您的期望进行操作。这样将来的实现就不能更改它,而您可以明确说明将发生什么。


9
这里涉及到sorted()distinct()的行为有关。如果流已排序,则排序操作是稳定的;否则不稳定。类似地,如果流已排序,distinct操作将返回每个等价类的第一个实例。如果流已排序,则规约也会遵守相遇顺序。缺少的是min()max()没有承诺这一点,但可以说它们应该这样做。 - Brian Goetz
@BrianGoetz,您认为未来的版本是否会致力于这样的事情?我遇到的类似问题是Collectors.toList()不承诺返回的List的类型或可变性。对于这种情况有保证将会非常好。 - Paul Boddington
11
这两个示例完全不同。有一个合理的观点认为最小/最大的事情只是疏忽,因此我们有可能会纠正它。toList() 的问题不是疏忽 - 这正是重点!因此我们不可能改变它。完全缺乏承诺是有意为之的,以最大化实现的灵活性。(我们提供了易于替代的方法。如果您想要一个 ArrayList,可以使用toCollection(ArrayList::new)。) - Brian Goetz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接