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

Java 如何使用流获取具有多个属性的最大值的对象?,java,java-stream,dry,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;

比如说我有一群恶棍。它们的特点是它们有多好、多坏或多丑

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 villains=new ArrayList();
添加(新反派(“鲍勃”,2,2,1));
添加(新反派(“查理”,2,1,2));
添加(新反派(“Dave”,2,1,1));
添加(新反派(“安迪”,2,2,2));
添加(新反派(“艾迪”,1,2,2));
添加(新反派(“弗兰兹”,1,2,1));
添加(新的反派(“家伙”,1,1,2));
添加(新反派(“哈利”,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();
列出最佳反派=反派
.stream()
.collect(分组方式(v->v.good,TreeMap::new,toList())
.lastEntry()
.getValue()
.stream()
.collect(分组方式(v->v.bad,TreeMap::new,toList())
.lastEntry()
.getValue()
.stream()
.collect(分组方式(v->v.ught,TreeMap::new,toList())
.lastEntry()
.getValue();
这实际上导致了一个只有一个成员的
列表
:Andy。他真的是最好的,最坏的,最丑的

然而,我有相当多的代码重复,收集值,将它们再次转换为流,等等。关于如何清理这些问题,有什么建议吗


JVM是如何处理这个问题的。或者引擎盖下面有什么神奇的东西吗?

您可以使用嵌套分组方式

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())))));
因此,这个想法是,它首先要看哪一个具有最高的价值 好,那么(在平局的情况下)坏的值最高, 然后(如果它仍然不是决定性的)哪一个具有 “丑陋”

对于
Villian
s,您更希望使用以下
Comparator
进行排序:

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

Villain result = villains.stream()
                         .max(villainComparator)
                         .orElse(null);
Comparator VillaComparator=Comparator.comparingInt(反派::getGood)
.然后比较(恶棍::getBad)
.然后比较(恶棍:变得丑陋);
恶棍结果=恶棍.stream()
.max(VillaComposator)
.orElse(空);

您需要的是一个比较器。您可以将其添加到流中。它将如下所示:

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());
List bestVillains=villains.stream()
.已排序((o1,o2)->{
如果(o2.good==o1.good){
如果(o2.bad==o1.bad){
返回o2.丑陋-o1.丑陋;
}否则{
返回o2.bad-o1.bad;
}
}否则{
返回o2.good-o1.good;
}
})
.限额(1)
.collect(Collectors.toList());

这就产生了一个恶棍名单——这群恶棍中最坏的一个。这里发生的情况是,比较器只按相反顺序排序,然后取第一个条目。

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

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

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

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

如果Eddy有
“Eddy”,3,1,2,
,那么您的回答会是什么
Andy
Eddy
?在这种情况下,它将是Eddy。因此,这个想法是,它首先查看哪个值为
,然后(在平局的情况下)哪个值为
,然后(如果它仍然不是决定性的)哪个值为“丑陋”最高。我更新了我的帖子来说明这一点。
List<Villain> bestVillainsMK2 = func.apply(func.apply(func.apply(villains, Villain::getGood), Villain::getBad), Villain::getUgly);
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);