首页 > 解决方案 > 如何在添加到地图之前从收集器中的方法调用中过滤数据

问题描述

我有结构

interface Foo{
   List<Bar> getBars();
}

interface Bar{
   List<Number> getValues();
}

并列出List<Foo> foos

我想要的是创建看起来或多或少像这样的地图,其中不包括 foo 没有 bar 或 bar 没有值的条目。

{
  fooid :{
              barId : bar
           }
}

显然我的第一个想法是做

    foos.stream().filter(f->!f.getBars().isEmpty())
    .collect( Collectors.toMap(
        f->f.id(),
        f->f.getBars().stream().filter(b->!b.getValues().isEmpty())
                 .collect(Collectors.toMap( b->b.id(),b->b.getValues()                       
    ))));

问题是我调用getValues了两次方法,这是一种昂贵的方法,处理这样的调用的最佳做法是什么?

标签: javajava-stream

解决方案


这就是你要找的:

private static void doMap(List<Foo> foos) {
    foos.stream()
        .filter(foo -> !foo.getBars()
                           .isEmpty())
        .map(foo -> new SimpleEntry<>(foo.getId(), foo.getBars()
                                                      .stream()
                                                      .map(bar -> new SimpleEntry<>(bar.getId(), bar.getValues()))
                                                      .filter(entry -> !entry.getValue()
                                                                             .isEmpty())
                                                      .collect(entriesToMapCollector())))
        .filter(entry -> !entry.getValue().isEmpty())
        .collect(entriesToMapCollector());
}

private static <K, V> Collector<Entry<K, V>, ?, Map<K, V>> entriesToMapCollector() {
    return Collectors.toMap(Entry::getKey, Entry::getValue);
}

(为了可读性、可维护性和一切能力,我强烈主张对此采取更命令式的方法——请不要在生产代码中这样做)


推荐阅读