英文:
Compare two lists based on highest value and subsequent values?
问题
我想比较每个列表,找到每个列表中的“最高”分数。如果两个最高分数相同,那么我想比较第二高分数,依此类推。我该怎么做?
英文:
I have two lists of:
List<Person> persons;
each Person has the attribute - score
e.g.
list1:
person.score = 3
person.score = 5
person.score = 8
list2:
person.score = 8
person.score = 4
person.score = 7
I want to compare each list to find the highest
score in each. If both highest are the same, then I want to compare the second highest etc and so on.
How can I do so?
答案1
得分: 1
你可以复制每个 List
,对这些副本进行排序,然后逐个元素进行比较。
英文:
You can make a copy of each List
, sort these copies, then compare them element by element.
Comparator<List<Person>> comp = (a, b) -> {
List<Person> s1 = a.stream().sorted(Comparator.comparingInt(Person::getScore).reversed()).toList(),
s2 = b.stream().sorted(Comparator.comparingInt(Person::getScore).reversed()).toList();
for (int i = 0; i < Math.min(a.size(), b.size()); i++) {
int c = Integer.compare(s1.get(i).getScore(), s2.get(i).getScore());
if (c != 0) return c;
}
return Integer.compare(a.size(), b.size());
};
答案2
得分: 0
定义一个比较器来比较这些列表。在排序每个列表后,比较器会在相应的分数不相等时提前终止,返回最后一次比较的值。否则,如果两个列表相等,或者它们的大小不同,较短的列表将返回为较小的。
细节
由于分数是比较的目标,我决定提前获取它并将其放在已排序的int
数组中。然后我反向迭代这些数组,以确保我首先测试最大的元素(IntStreams
不能采用比较器进行排序,而且我不想包装int
值)。
在遍历列表时,分数进行比较,并将结果映射到流中。然后比较的结果(-1、0或1)被过滤以忽略零值,将结果捕获在OptionalInt
中。
然后要么返回OptionalInt
值,要么比较数组大小。然后,如果所有比较都相等,较小的数组长度将返回-1,较大的将返回1,相等的长度将返回0。
Comparator<List<Person>> listComp = (lst1, lst2) -> {
int[] scores1 = lst1.stream().mapToInt(Person::getScore).sorted()
.toArray();
int[] scores2 = lst2.stream().mapToInt(Person::getScore).sorted()
.toArray();
OptionalInt diff = IntStream
.iterate(Math.min(scores1.length-1, scores2.length-1),
i -> i >= 0, i -> i-1)
.map(i -> Integer.compare(scores1[i], scores2[i]))
.filter(delta -> delta != 0).findFirst();
return diff.orElse(Integer.compare(scores1.length, scores2.length));
};
演示
一些数据 - 比较列表中的每两个列表。出于演示目的,我使用了一个具有修改的toString()
的record
。
record Person(int getScore) {
@Override
public String toString() {
return "%d".formatted(getScore);
}
}
List<List<Person>> lists = List.of(
// 第一个较小
List.of(new Person(3), new Person(5), new Person(8)),
List of(new Person(8), new Person(4), new Person(7)),
// 相等的列表
List of(new Person(3), new Person(7), new Person(8)),
List of(new Person(8), new Person(3), new Person(7)),
// 由于长度而较小
List of(new Person(3), new Person(7), new Person(8)),
List of(new Person(8), new Person(3), new Person(7),
new Person(2)));
要比较这些列表,请使用int result = listComp.compare(list1, list2);
int v;
for (int i = 0; i < lists.size(); i += 2) {
System.out.println(lists.get(i)
+ ((v = listComp.compare(lists.get(i), lists.get(i + 1))) > 0
? " > "
: (v < 0) ? " < " : " == ")
+ lists.get(i + 1));
}
输出如下:
[3, 5, 8] < [8, 4, 7]
[3, 7, 8] == [8, 3, 7]
[3, 7, 8] > [8, 3, 7, 2]
英文:
Define a comparator to compare the lists. After, sorting each list, The comparator short circuits as soon corresponding scores are unequal, returning the value of the the last comparison. Otherwise, if the two lists are equal or if if their sizes are different, the shortest list will return as the smaller.
Details
Since the score is the target of the comparison, I decided to grab it early and place in int
arrays which are sorted. Then I iterate over the arrays in reverse order to ensure I am testing the largest elements first (IntStreams
can't take a comparator for sorting and I didn't want to box the int
values).
Iterating over the lists, the scores are compared and the result is mapped to the stream. Then the result of the comparison (-1,0, or 1)
is filtered to ignore zero values, capturing the result in an OptionalInt
.
Then either the OptionalInt
value is returned, or the comparison of the array sizes. Then, all comparisons being equal, the smaller array length would return -1
, larger 1
, and equal lengths 0
Comparator<List<Person>> listComp = (lst1, lst2) -> {
int[] scores1 = lst1.stream().mapToInt(Person::getScore).sorted()
.toArray();
int[] scores2 = lst2.stream().mapToInt(Person::getScore).sorted()
.toArray();
OptionalInt diff = IntStream
.iterate(Math.min(scores1.length-1, scores2.length-1),
i -> i >= 0, i -> i-1)
.map(i -> Integer.compare(scores1[i], scores2[i]))
.filter(delta -> delta != 0).findFirst();
return diff.orElse(Integer.compare(scores1.length, scores2.length));
};
Demos
Some Data - comparing every two lists in the list of lists to each other. For demo purposes, I am using a record
with a modified toString()
.
record Person(int getScore) {
@Override
public String toString() {
return "%d".formatted(getScore);
}
}
List<List<Person>> lists = List.of(
// first is smaller
List.of(new Person(3), new Person(5), new Person(8)),
List.of(new Person(8), new Person(4), new Person(7)),
// equal lists
List.of(new Person(3), new Person(7), new Person(8)),
List.of(new Person(8), new Person(3), new Person(7)),
// first is smaller due to length
List.of(new Person(3), new Person(7), new Person(8)),
List.of(new Person(8), new Person(3), new Person(7),
new Person(2)));
To compare the lists, use int result = listComp.compare(list1, list2);
int v;
for (int i = 0; i < lists.size(); i += 2) {
System.out.println(lists.get(i)
+ ((v = listComp.compare(lists.get(i), lists.get(i + 1))) > 0
? " > "
: (v < 0) ? " < " : " == ")
+ lists.get(i + 1));
}
prints the following:
[3, 5, 8] < [8, 4, 7]
[3, 7, 8] == [8, 3, 7]
[3, 7, 8] > [8, 3, 7, 2]
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论