RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1514957
Accepted
Nowhere Man
Nowhere Man
Asked:2023-04-22 05:07:00 +0000 UTC2023-04-22 05:07:00 +0000 UTC 2023-04-22 05:07:00 +0000 UTC

如何使用 Stream API 获取集合的最大值列表

  • 772

最近有人问了一个问题,归结为对于给定的哈希表,Map<Person, AtomicInteger>需要从Person最大值为 的键中找到名称AtomicInteger / Integer。

该解决方案显示了使用 查找单个最大值的示例,以及使用普通哈希表迭代查找可以具有相同最大值Stream::max(Comparator<? super T> cmp)的名称列表的示例:

record Result(Integer max, List<String> names) {}

public static Result getNamesWithMaxValue(Map<Person, AtomicInteger> map) {
    int max = Integer.MIN_VALUE;
    List<String> names = new ArrayList<>();
    for (Map.Entry<Person, AtomicInteger> e : map.entrySet()) {
        int curr = e.getValue().get();
        if (curr >= max) {
            if (curr > max) {
                max = curr;
                names.clear();
            }
            names.add(e.getKey().getName());
        }
    }
    return new Result(max, names);
}

问题来了,如何使用 Stream API 获取所有最大值(或最小值)的列表,以及与常规循环相比,这种解决方案的效率如何?希望该解决方案是通用的,而不是针对上述问题的特定情况。

java
  • 2 2 个回答
  • 46 Views

2 个回答

  • Voted
  1. Best Answer
    Nowhere Man
    2023-04-22T05:07:00Z2023-04-22T05:07:00Z

    有几种可能的解决方案:

    1. 首先确定最大元素,然后在第二遍过滤掉等于最大值的值:
    public static<T,R> List<R> maxList(Collection<T> data, Comparator<? super T> cmp, Function<T, R> converter) {
        return data.stream().max(cmp)   // определить максимум
            .map(max -> data.stream()   // второй проход
                .filter(x -> cmp.compare(x, max) == 0)  // фильтр максимумов
                .map(converter::apply)  // преобразование в нужный тип результата
                .collect(Collectors.toList())
            ).orElseGet(Collections::emptyList);
    }
    
    1. 使用自定义收集器 在这里,它将创建列表,过滤掉最大值,合并多个列表(在使用并行流的情况下),使用转换器函数将列表转换为列表。Collector.of(Supplier<A> supplier, BiConsumer<A,T> accumulator, BinaryOperator<A> combiner, Function<A,R> finisher)
      Supplier<A>accumulatorcombinerfinisherList<T>List<R>
    public static<T,R> List<R> maxList(Collection<T> data, Comparator<? super T> cmp, Function<T, R> converter) {
        return data.stream()
            .collect(Collector.of(
                ArrayList::new,
                (List<T> list, T i) -> {
                    if (list.isEmpty()) {
                        list.add(i);
                    } else {
                        int c = cmp.compare(i, list.get(0));
                        if (c >= 0) {
                            if (c > 0) list.clear();
                            list.add(i);
                        }
                    }
                },
                (lst1, lst2) -> {
                    if (lst1.isEmpty()) return lst2;
                    else if (lst2.isEmpty()) return lst1;
                    int c = cmp.compare(lst1.get(0), lst2.get(0));
                    if (c == 0) {
                        lst1.addAll(lst2);
                        return lst1;
                    }
                    return c > 0 ? lst1 : lst2;
                },
                lst -> lst.stream().map(converter::apply).collect(Collectors.toList())
            ));
    }
    
    1. 按某个键分组得到一个哈希表,然后找到键的最大值。
      在这种情况下,我们得到了双重传递(在最坏的情况下)——首先遍历输入集合,然后遍历一组组——然后为这些组生成中间列表。
    public static<T,M extends Comparable,R> List<R> maxListGroup(
        Collection<T> data, Function<T, R> converter, Function<T, M> maxConverter
    ) {
        Optional<Map.Entry<M, List<R>>> max = data.stream()
            .collect(Collectors.groupingBy(
                maxConverter,
                Collectors.mapping(converter, Collectors.toList())
            )) // Map<M, List<R>>
            .entrySet()
            .stream()
            .max(Map.Entry.comparingByKey());
        return max
            .map(Map.Entry::getValue)
            .orElseGet(Collections::emptyList);
    }
    

    要搜索最小值,您应该Stream::min在第一个变体中使用,在第二个变体中,将比较符号从“更多”更改为“更少”。


    同样,如果除了结果列表之外你还想获得最大值,如原题,你可以实现一个包装类并添加另一个转换器函数,将输入集合中的数据转换为所需的结果:

    record Result<M, R>(M max, List<R> data){}
    
    public static<T,M,R> Result<M, List<R>> maxResultTwoPass(
        Collection<T> data, Comparator<? super T> cmp, 
        Function<T, R> converter, Function<T, M> maxConverter
    ) {    
        return data.stream().max(cmp)
            .map(max -> new Result(
                    maxConverter.apply(max),
                    data.stream()
                    .filter(x -> cmp.compare(x, max) == 0)
                    .map(converter::apply)
                    .collect(Collectors.toList())
                )
            ).orElse(null);
    }
    

    在带有自定义收集器的版本中,它略有修改finisher:

    public static<T,M,R> Result<M, List<R>> maxResult(
        Collection<T> data, Comparator<? super T> cmp, 
        Function<T, R> converter, Function<T, M> maxConverter
    ) {    
        return data.stream()
            .collect(Collector.of(
                ArrayList::new,
                (List<T> list, T i) -> {
                    if (list.isEmpty()) {
                        list.add(i);
                    } else {
                        int c = cmp.compare(i, list.get(0));
                        if (c >= 0) {
                            if (c > 0) list.clear();
                            list.add(i);
                        }
                    }
                },
                (lst1, lst2) -> {
                    if (lst1.isEmpty()) return lst2;
                    else if (lst2.isEmpty()) return lst1;
                    int c = cmp.compare(lst1.get(0), lst2.get(0));
                    if (c == 0) {
                        lst1.addAll(lst2);
                        return lst1;
                    }
                    return c > 0 ? lst1 : lst2;
                },
                lst -> lst.isEmpty() 
                        ? null 
                        : new Result(
                            maxConverter.apply(lst.get(0)), 
                            lst.stream().map(converter::apply).collect(Collectors.toList())
                        )
            ));
    }
    

    在带有分组的变体中,结果被修改(或者你可以返回Map.Entry):

    public static<T,M extends Comparable,R> Map.Entry<M, List<R>> maxEntryGroup(
        Collection<T> data, Function<T, R> converter, Function<T,M> maxConverter
    ) {
        Optional<Map.Entry<M, List<R>>> max = data.stream()
            .collect(Collectors.groupingBy(
                maxConverter,
                Collectors.mapping(converter, Collectors.toList())
            )) // Map<M, List<R>>
            .entrySet()
            .stream()
            .max(Map.Entry.comparingByKey());
        return max.orElse(null);
    }
    
    public static<T,M extends Comparable,R> Result<M, List<R>> maxResultGroup(
        Collection<T> data, Function<T, R> converter, Function<T,M> maxConverter
    ) {
        Optional<Map.Entry<M, List<R>>> max = data.stream()
            .collect(Collectors.groupingBy(
                maxConverter,
                Collectors.mapping(converter, Collectors.toList())
            )) // Map<M, List<R>>
            .entrySet()
            .stream()
            .max(Map.Entry.comparingByKey());
        return max
            .map(e -> new Result(e.getKey(), e.getValue()))
            .orElse(null);
    }
    

    测试:

    record Person(String name, int age) {}
    
    public static void main(String args[]) {
        Map<Person, AtomicInteger> map = Map.of(
            new Person("John", 33), new AtomicInteger(1000),
            new Person("Joan", 27), new AtomicInteger(4000),
            new Person("Jane", 24), new AtomicInteger(7500),
            new Person("Jack", 29), new AtomicInteger(7500),
            new Person("Jeff", 31), new AtomicInteger(1000),
            new Person("Jess", 28), new AtomicInteger(7500)
        );
        
        Function<Map.Entry<Person, AtomicInteger>, String> fun = e -> e.getKey().name();
        Function<Map.Entry<Person, AtomicInteger>, Integer> res = e -> e.getValue().get();
        
        System.out.println(maxList(
            map.entrySet(),
            Map.Entry.<Person, AtomicInteger>comparingByValue(Comparator.comparingInt(AtomicInteger::get)), 
            fun
        ));
    
        System.out.println(maxResult(
            map.entrySet(),
            Map.Entry.<Person, AtomicInteger>comparingByValue(Comparator.comparingInt(AtomicInteger::get)), 
            fun,
            res
        ));
    
        System.out.println(maxEntryGroup(
            map.entrySet(),
            fun,
            res
        ));
    }
    
    
    [Jess, Jack, Jane]
    Result[max=7500, data=[Jess, Jack, Jane]]
    7500=[Jess, Jack, Jane]
    

    对于空输入集合:

    []
    null
    null
    

    主要 SO 上的类似问题:

    • 如何强制max返回Java Stream中的所有最大值?
    • 2
  2. Pak Uula
    2023-04-22T15:21:00Z2023-04-22T15:21:00Z

    跟进@NowhereMan 的回答。

    您可以为以下产品制作定制电池Stream.reduce:

    public class MaxKeyAccumulator<K, V> {
        public V max;
        public List<K> keys;
        public final Comparator<V> cmp;
    
        public MaxKeyAccumulator(Comparator<V> cmp, V min) {
            super();
            keys = new LinkedList<>();
            max = min;
            this.cmp = cmp;
        }
        public <K1 extends K, V1 extends V> void accept(K1 key, V1 value) {
            if (cmp.compare(value, max) < 0) {
                return;
            }
            if (cmp.compare(value, max) == 0) {
                keys.add(key);
                return ;
            }
            max = value;
            keys.clear();
            keys.add(key);
        }
        public MaxKeyAccumulator<K,V> merge(MaxKeyAccumulator<K,V> other) {
            if (this.cmp.compare(other.max, this.max) < 0) {
                return this;
            }
            if (this.cmp.compare(other.max, this.max) > 0) {
                return other;
            }
            this.keys.addAll(other.keys);
            return this;
        }
    }
    

    然后可以这样写一个将人映射到整数的示例:

        public static void main(String[] args) {
            Map<Person, Integer> map = Map.of(
                new Person("John", 33), 1000,
                new Person("Joan", 27), 4000,
                new Person("Jane", 24), 7500,
                new Person("Jack", 29), 7500,
                new Person("Jeff", 31), 1000,
                new Person("Jess", 28), 7500
            );
            
            var result = map.entrySet()
                .stream()
                .reduce(
                    new MaxKeyAccumulator<Person, Integer>(Integer::compare, Integer.MIN_VALUE), 
                    (acc, entry) -> { acc.accept(entry.getKey(), entry.getValue()); return acc; }, 
                    (acc1, acc2) -> acc1.merge(acc2)
                );
            System.out.println(""+result.max +":"+ result.keys);
        }
    

    结果:

    7500:[Person("Jack",29), Person("Jess",28), Person("Jane",24)]
    
    • 2

相关问题

  • wpcap 找不到指定的模块

  • 如何以编程方式从桌面应用程序打开 HTML 页面?

  • Android Studio 中的 R.java 文件在哪里?

  • HashMap 初始化

  • 如何使用 lambda 表达式通过增加与原点的距离来对点进行排序?

  • 最大化窗口时如何调整元素大小?

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5