如何使用流获取具有多个属性最大值的对象?

时间:2019-02-08 14:13:36

标签: java java-stream dry

说我有一堆小人。它们的特点是好,坏或丑陋。

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<Villain>只有一个成员:Andy。他确实是最好,最坏,最丑的!

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

JVM如何处理它。顺序地还是引擎盖下面有魔术?

4 个答案:

答案 0 :(得分:5)

  

因此,想法是先查看哪个具有最高的价值   好,然后(如果是平局)则具有最高的坏值,   那么(如果仍然不是决定性的)   “丑陋”

您宁愿使用以下Comparator进行Villian排序:

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

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

答案 1 :(得分: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());

答案 2 :(得分:2)

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

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个恶棍的列表-最坏的一个。这里发生的是比较器只进行反向排序,然后您输入第一个条目。

答案 3 :(得分:1)

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

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类中添加了假想的吸气剂。

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

这里是专用功能

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

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