如何按对象的 ArrayList<Integer> 值对 ArrayList 进行排序?

问题描述 投票:0回答:2

假设对象有

ArrayList<Integer>
并且这些对象的 ArrayList 需要按其
ArrayList<Integer>
值排序,以便 ArrayList 的第一个索引具有最高优先级,如果两个对象的列表具有相同的第一个索引,则比较第二个索引等。那要怎么做呢?

例如,想象一个

OlympicWinner
类,其中包含奥运会冠军的
name
以及他们赢得的
medals

public Class OlympicWinner {

//contains medals in order: [gold, silver, bronze]
private ArrayList<Integer> medals
private String name;

OlympicWinner (String name, int goldMedals, int silverMedals, int bronzeMedals) {
this.name = name;
this.medals.add(goldMedals);
this.medals.add(silverMedals);
this.medals.add(bronzeMedals);
}

public String getName() {return this.name;}

public ArrayList<Integer> getMedals() {return this.medals;}
}

现在,如果有一个

ArrayList<OlympicWinner> olympicWinners
列表,并且该列表需要按
ArrayList<Integer> medals
排序,以便列表中的第一个是
OlympicWinner
,它拥有最多的黄金,如果两个拥有相同数量的黄金,则获得银牌最多的一方将是第一名,如果两者的银牌数量相同,则获得铜牌多的一方将是第一名。如何实施?

我可以这样想:

Collections.sort(olympicWinners, new Comparator<OlympicWinner> () {
    @Override
    public int compare(OlympicWinner a, OlympicWinner b) {
        //comparison ...

        //ridiculous if comparison
        if(a.getMedals().get(0) > b.getMedals().get(0)) {
            return 1;
        } else if (a.getMedals().get(0) < b.getMedals().get(0)) {
            return -1;
        } else { //both have equal amount of gold medals, then compares silvers
            if(a.getMedals().get(1) > b.getMedals().get(1)) {
                return 1;
            } else if (a.getMedals().get(1) < b.getMedals().get(1)) {
                return -1;
            } else {} // and so on
        }

    } // comparison ends here    
});

但我不确定如何实现

ArrayList<Integer>
的比较方法,也不知道这种排序方法
Collections.sort()
作为一个整体是否是最好的方法。荒谬的 if 方法不仅看起来很糟糕,而且不适用于该方法的抽象版本,其中
ArrayList<Integer>
的长度可能为 n。 (其中 n 是任意整数)

java sorting arraylist compare
2个回答
0
投票

让我们把这个问题分成两个单独的部分:

  1. 找到一种基于提供一些可以派生任何对象属性的代码来对对象列表进行排序的方法,并且我们希望对派生属性进行排序。
  2. 按照您描述的方式对
    List<Integer>
    对象列表进行排序:第一个整数获胜;仅当第一个相等时才查看第二个,依此类推。

对整数列表进行排序

List<List<Integer>> example = new ArrayList<List<Integer>>();
example.add(List.of(2, 1, 1, 1, 1));
example.add(List.of(1, 2, 3, 4, 6));
example.add(List.of(1, 2, 3, 4, 5));

example.sort((a, b) -> {
  for (int i = 0; i < Math.min(a.size(), b.size()); i++) {
    int c = a.get(i).compareTo(b.get(i));
    if (c != 0) return c;
  }
  // We ran out of elements; the larger list thus wins
  return Integer.compare(a.size(), b.size());
});

for (var list : example) System.out.println(list);

打印:

[1, 2, 3, 4, 5]
[1, 2, 3, 4, 6]
[2, 1, 1, 1, 1]

这样就可以了!

现在我们可以做到这一点,我们可以简单地求解 1:

对派生属性进行排序

class Foo {
  List<Integer> ints;

  Foo(int... args) {
    this.ints = new ArrayList<Integer>();
    for (int arg : args) this.ints.add(arg);
  }

  @Override public String toString() {
    return ints.toString();
  }
}

List<Foo> example = new ArrayList<>();
example.add(new Foo(1, 2, 3, 4, 6));
example.add(new Foo(1, 2, 3, 4, 5));
example.add(new Foo(2, 1, 1, 1, 1));

Comparator<List<Integer>> intListComparator = (a, b) -> {
  for (int i = 0; i < Math.min(a.size(), b.size()); i++) {
    int c = a.get(i).compareTo(b.get(i));
    if (c != 0) return c;
  }
  // We ran out of elements; the larger list thus wins
  return Integer.compare(a.size(), b.size());
};

example.sort(Comparator.comparing(f -> f.ints, intListComparator));

for (var f : example) System.out.println(f);
  • 第二个片段中的比较器是从第 1 部分复制粘贴的。
  • Comparator.comparing(x, y)
    就是这里的魔力。
    x
    是一个导出属性的函数。在这里,给定一个
    f
    ,它会导出整数列表。
    y
    是一个比较器,可以通过返回正数、负数或 0 来决定给定任何 2 个派生属性(此处给定 2 个整数列表),哪一个“更高”/如果它们按顺序相同。

0
投票

您对实施

Comparator
的想法是正确的,但这里有一些可以(应该?)改进的地方。

首先,不需要手动比较每组对应的奖牌。您可以使用

Integer.compare
并让它为您完成繁重的工作。
其次,正如您所指出的,这里有很多样板代码,其中金牌、银牌和铜牌的逻辑相同,并且如果有更多类型的奖牌,情况只会更糟 - 正如您已经指出的那样。处理这个问题的方法是使用循环 - 您可以迭代奖牌类型,一旦您击中了一对具有不同数字的奖牌,就返回比较结果。如果不这样做,则两个
OlympicWinner
是等效的:

Comparator<OlympicWinner> medalsComparator = new Comparator<OlympicWinner>() {
    @Override
    public int compare(OlympicWinner o1, OlympicWinner o2) {
        // Don't assume how many medals an OlypicWinner has, 
        // but assume o1 and o2 have the same length of the medals member
        List<Integer> m1 = o1.getMedals();
        List<Integer> m2 = o2.getMedals();
        for (int i = 0; i < m1.size(); ++i) {
            int res = Integer.compare(m1.get(i), m2.get(i));
            if (res != 0) {
                return res;
            }
        }

        // No pair of corresponding medals has a different count,
        // so o1 and o2 must be equivalent
        return 0;
    }

如评论中所述,该比较器并不假设只有三种类型的奖牌,而是假设两个

OlympicWinner
具有相同数量的类型(即
medals
列表的大小相同)。如果这个假设并不总是正确,则应该修改代码以适应它,但为了保持示例简单,我采用了这个假设。

© www.soinside.com 2019 - 2024. All rights reserved.