【问题标题】:How to get the object that has the maximum value for multiple attributes using a stream?如何使用流获取具有多个属性最大值的对象?
【发布时间】:2019-02-08 14:13:36
【问题描述】:

假设我有一群恶棍。它们的特点是它们的好、坏或丑。

static class Villain {
    String name;
    int good;
    int bad;
    int ugly;

    Villain(String name, int good, int bad, int ugly) {
        this.name = name;
        this.good = good;
        this.bad = bad;
        this.ugly = ugly;
    }
}

好的,见帮派:

List<Villain> villains = new ArrayList<>();
villains.add(new Villain("Bob", 2, 2, 1));
villains.add(new Villain("Charley", 2, 1, 2));
villains.add(new Villain("Dave", 2, 1, 1));
villains.add(new Villain("Andy", 2, 2, 2));
villains.add(new Villain("Eddy", 1, 2, 2));
villains.add(new Villain("Franz", 1, 2, 1));
villains.add(new Villain("Guy", 1, 1, 2));
villains.add(new Villain("Harry", 1, 1, 1));

我想做的是,我想弄清楚谁是最好的、最坏的和最丑的。有了这个,我的意思是找出谁是最好的。在平局的情况下,谁是最糟糕的。如果打平,谁最丑。

我已经成功做到了,代码如下。

List<Villain> bestVillains = villains
        .stream()
        .collect(groupingBy(v -> v.good, TreeMap::new, toList()))
        .lastEntry()
        .getValue()
        .stream()
        .collect(groupingBy(v -> v.bad, TreeMap::new, toList()))
        .lastEntry()
        .getValue()
        .stream()
        .collect(groupingBy(v -> v.ugly, TreeMap::new, toList()))
        .lastEntry()
        .getValue();

确实,这导致List&lt;Villain&gt; 只有一个成员:Andy。他真的是最好的,最坏的,最丑的!

但是,我有很多代码重复、收集值、再次将它们转换为流等。关于如何清理这些有什么建议吗?

JVM 是如何处理的。依次或在引擎盖下发生了什么魔法?

【问题讨论】:

  • 如果 Eddy 有"Eddy", 3, 1, 2, 会怎样,你会怎么回答? AndyEddy?
  • 在这种情况下,应该是 Eddy。所以这个想法是,它首先查看good 的值最高,然后(在平局的情况下)bad 的值最高,然后(如果它仍然不是决定性的)具有最高的“丑陋”的价值。我更新了我的帖子以明确这一点。

标签: java java-stream dry


【解决方案1】:

所以这个想法是它首先查看哪个具有最高的价值 好,然后(在平局的情况下)坏的价值最高, 那么(如果它仍然不是决定性的)哪个具有最高的价值 “丑”

您希望使用以下ComparatorVillians 进行排序:

Comparator<Villain> villainComparator = Comparator.comparingInt(Villain::getGood)
    .thenComparingInt(Villain::getBad)
    .thenComparingInt(Villain::getUgly);

Villain result = villains.stream()
                         .max(villainComparator)
                         .orElse(null);

【讨论】:

    【解决方案2】:

    您可以使用嵌套分组方式

    TreeMap<Integer, TreeMap<Integer, TreeMap<Integer, List<Villain>>>> collect = 
        villains.stream()
            .collect(groupingBy(v -> v.good, TreeMap::new,
                         groupingBy(v -> v.bad, TreeMap::new,
                             groupingBy(v -> v.ugly, TreeMap::new, mapping(o -> o, toList())))));
    

    然后打印出来:

    System.out.println(collect.lastEntry().getValue()
                          .lastEntry().getValue()
                              .lastEntry().getValue());
    

    【讨论】:

      【解决方案3】:

      您需要的是一个比较器。您可以将其添加到您的信息流中。它看起来像这样:

      List<Villain> bestVillains = villains.stream()
              .sorted((o1, o2) -> {
                  if(o2.good == o1.good){
                      if(o2.bad == o1.bad){
                          return o2.ugly - o1.ugly;
                      }else{
                          return o2.bad - o1.bad;
                      }
                  }else{
                      return o2.good - o1.good;
                  }
              })
              .limit(1)
              .collect(Collectors.toList());
      

      这会产生一个包含 1 个恶棍的列表 - 最糟糕的一群。这里发生的是比较器仅反向排序,然后您获取第一个条目。

      【讨论】:

        【解决方案4】:

        如果您只是担心代码重复,请尝试提取代码并重用它,例如:

        final BiFunction<List<Villain>, Function<Villain, Object>, List<Villain>> func = (listVillians, villianAttribute) -> listVillians
            .stream()
            .collect(groupingBy(villianAttribute, TreeMap::new, toList()))
            .lastEntry()
            .getValue();
        

        然后像这样使用它:

        List<Villain> bestVillainsMK2 = func.apply(func.apply(func.apply(villains, Villain::getGood), Villain::getBad), Villain::getUgly);
        

        注意:我在 Villain 类中添加了虚构的 getter。

        最里面的调用使用原始列表,其他调用使用这些函数的返回。

        这里作为专用功能

        private static List<Villain> func2(List<Villain> listVillians, Function<Villain, Object> villianAttribute) {
            return listVillians.stream()
              .collect(groupingBy(villianAttribute, TreeMap::new, toList()))
              .lastEntry()
              .getValue();
        }
        

        用途几乎相同

        List<Villain> bestVillainsMK3 = func2(func2(func2(villains, Villain::getGood), Villain::getBad), Villain::getUgly);
        

        但如果您也对适合您情况的正确工具或模式感兴趣,请参阅@nullpointers 使用比较器的方法。

        【讨论】:

          猜你喜欢
          • 2017-09-20
          • 2019-07-02
          • 1970-01-01
          • 2010-10-29
          • 1970-01-01
          • 2020-08-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多