如何强制max()返回Java流中的所有最大值? [英] How to force max() to return ALL maximum values in a Java Stream?

查看:176
本文介绍了如何强制max()返回Java流中的所有最大值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经测试了一个位于Java 8 lambdas和stream上的max()函数,似乎在执行max()的情况下,即使多个对象比较为0,它也会返回一个任意的元素。

I've tested a bit the max() function on Java 8 lambdas and streams, and it seems that in case max() is executed, even if more than one object compares to 0, it returns an arbitrary element within the tied candidates without further consideration.

这样的最大预期行为是否有明显的技巧或函数,以便返回所有最大值?我在API中没有看到任何内容,但我确定它必须比手动比较更好。

Is there an evident trick or function for such a max expected behavior, so that all max values are returned? I don't see anything in the API but I am sure it must exist something better than comparing manually.

例如:

//myComparator is an IntegerComparator
Stream.of(1,3,5,3,2,3,5).max(myComparator).forEach(System.out::println);
//Would print 5,5 in any order.


推荐答案

我相信OP正在使用Comparator来分区输入到等价类中,并且期望的结果是根据该比较器的最大值的等价类的成员的列表。

I believe the OP is using a Comparator to partition the input into equivalence classes, and the desired result is a list of members of the equivalence class that is the maximum according to that Comparator.

不幸的是,使用 int 值作为一个示例问题是一个可怕的例子。所有等于 int 的值都是可以替换的,因此没有保留等价值排序的概念。也许一个更好的例子是使用字符串长度,其中所需的结果是返回一个输入中的字符串列表,所有的输入都具有最长的长度。

Unfortunately, using int values as a sample problem is a terrible example. All equal int values are fungible, so there is no notion of preserving the ordering of equivalent values. Perhaps a better example is using string lengths, where the desired result is to return a list of strings from an input that all have the longest length within that input.

给定一个输入集合,例如

Given an input collection, say

List<String> list = ... ;

这样做足够简单,两次通过,第一次获得最长的长度,第二次以筛选具有该长度的字符串:

it's simple enough to do this in two passes, the first to get the longest length, and the second to filter the strings that have that length:

int longest = list.stream()
                  .mapToInt(String::length)
                  .max()
                  .orElse(-1);

List<String> result = list.stream()
                          .filter(s -> s.length() == longest)
                          .collect(toList());

如果输入是流,不能多次遍历,可以使用收集器仅计算一次通过的结果。写这样的收集器并不困难,但它是有点乏味,因为有几种情况下处理。给定比较器时生成这样的收集器的帮助函数如下:

If the input is a stream, which cannot be traversed more than once, it is possible to compute the result in only a single pass using a collector. Writing such a collector isn't difficult, but it is a bit tedious as there are several cases to be handled. A helper function that generates such a collector, given a Comparator, is as follows:

static <T> Collector<T,?,List<T>> maxList(Comparator<? super T> comp) {
    return Collector.of(
        ArrayList::new,
        (list, t) -> {
            int c;
            if (list.isEmpty() || (c = comp.compare(t, list.get(0))) == 0) {
                list.add(t);
            } else if (c > 0) {
                list.clear();
                list.add(t);
            }
        },
        (list1, list2) -> {
            if (list1.isEmpty()) {
                return list2;
            } 
            if (list2.isEmpty()) {
                return list1;
            }
            int r = comp.compare(list1.get(0), list2.get(0));
            if (r < 0) {
                return list2;
            } else if (r > 0) {
                return list1;
            } else {
                list1.addAll(list2);
                return list1;
            }
        });
}

这将中间结果存储在 ArrayList 。不变量是任何这样的列表内的所有元素在比较器方面是等效的。当添加一个元素,如果它小于列表中的元素,它被忽略;如果相等,就添加;如果更大,则清空列表并添加新元素。合并也不是太困难:返回具有较大元素的列表,但如果它们的元素相等,则添加列表。

This stores intermediate results in an ArrayList. The invariant is that all elements within any such list are equivalent in terms of the Comparator. When adding an element, if it's less than the elements in the list, it's ignored; if it's equal, it's added; and if it's greater, the list is emptied and the new element is added. Merging isn't too difficult either: the list with the greater elements is returned, but if their elements are equal the lists are appended.

给定输入流,很容易使用:

Given an input stream, this is pretty easy to use:

Stream<String> input = ... ;

List<String> result = input.collect(maxList(comparing(String::length)));

这篇关于如何强制max()返回Java流中的所有最大值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆