如何强制max返回Java流中的所有最大值?

2022-08-31 23:56:08

我已经在Java 8 lambdas和streams上测试了一下该函数,似乎在执行的情况下,即使多个对象与0进行比较,它也会在绑定的候选项中返回任意元素而无需进一步考虑。maxmax

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

例如:

// 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.

答案 1

我相信OP正在使用a将输入划分为等价类,并且所需的结果是等价类的成员列表,该成员是该类的最大值。ComparatorComparator

不幸的是,使用值作为示例问题是一个可怕的例子。所有相等的值都是可互换的,因此不存在保留等效值的顺序的概念。也许一个更好的例子是使用字符串长度,其中所需的结果是从输入返回字符串列表,这些字符串在该输入中具有最长的长度。intint

我不知道有什么方法可以在不将至少部分结果存储在集合中的情况下执行此操作。

给定一个输入集合,比如说

List<String> list = ... ;

...在两次传递中执行此操作非常简单,第一次用于获取最长长度,第二次用于筛选具有该长度的字符串:

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

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

如果输入是不能多次遍历的流,则可以使用收集器仅计算单个传递的结果。编写这样的收集器并不困难,但有点乏味,因为有几个情况需要处理。在给定 的情况下,生成此类收集器的帮助器函数如下所示:Comparator

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;
            }
        });
}

这会将中间结果存储在 .不变量是任何此类列表中的所有元素在 .添加元素时,如果它小于列表中的元素,则忽略它;如果相等,则添加;如果它更大,则清空列表并添加新元素。合并也不是太难:返回具有较大元素的列表,但如果它们的元素相等,则会追加列表。ArrayListComparator

给定一个输入流,这非常易于使用:

Stream<String> input = ... ;

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

答案 2

我将按值分组并将值存储到a中以便对我的值进行排序,然后我将通过获取最后一个条目来获得最大值,如下所示:TreeMap

Stream.of(1, 3, 5, 3, 2, 3, 5)
    .collect(groupingBy(Function.identity(), TreeMap::new, toList()))
    .lastEntry()
    .getValue()
    .forEach(System.out::println);

输出:

5
5