不能使用单一的Comparator
进行排序。
你需要:
- 按名称对数据进行分组
- 对每个分组内的元素按照得分从高到低排序
- 然后需要将分组展平并转换为列表
利用Java 8
编辑:自从我写下这个答案之后,Java 8已经发布了,这大大简化了问题:
import java.util.*;
import static java.util.Comparator.*;
import static java.util.stream.Collectors.*;
List<Record> result = records.stream()
.sorted(comparingInt(Record::getScore).reversed())
.collect(groupingBy(Record::getName, LinkedHashMap::new, toList()))
.values().stream()
.flatMap(Collection::stream)
.collect(toList());
首先我们按照分数降序排序,然后使用 LinkedHashMap
进行分组,这样可以保留键的插入顺序,因此拥有更高分数的键将排在前面。
如果组比较小,则先排序是可以的,因此不同组之间对象之间冗余的比较不会太耗费时间。
此外,使用这种方法还可以保留重复项。
或者,如果您不介意保留重复项,可以:
Comparator<Record> highestScoreFirst = comparingInt(Record::getScore).reversed();
List<Record> result = records.stream()
.collect(groupingBy(Record::getName,
toCollection(() -> new TreeSet<>(highestScoreFirst))))
.values().stream()
.sorted(comparing(SortedSet::first, highestScoreFirst))
.flatMap(Collection::stream)
.collect(toList());
如果记录被分组到已排序的TreeSet
中,而不是将值作为流的第一个操作进行排序,然后按其第一个最高值对集合进行排序。
如果组很大,分组再排序是适当的,以减少冗余比较。
实现Comparable
:
您可以通过让记录实现Comparable
来缩短代码。
public class Record implements Comparable<Record> {
@Override
public int compareTo(Record other) {
return -Integer.compare(getScore(), other.getScore());
}
...
}
List<Record> result = records.stream()
.collect(groupingBy(Record::getName, toCollection(TreeSet::new)))
.values().stream()
.sorted(comparing(SortedSet::first))
.flatMap(Collection::stream)
.collect(toList());
Java 8之前
编辑:下面是一个演示一种方法的非常基础的单元测试。我没有像我希望的那样对其进行过多的清理。
在Java中处理这样的事情很麻烦,而我通常会使用Google Guava来完成。
import org.junit.Test;
import java.util.*;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
public class GroupSortTest {
@Test
public void testGroupSort() {
List<Record> records = asList(
new Record("a", 3),
new Record("a", 9),
new Record("b", 7),
new Record("b", 10),
new Record("c", 8),
new Record("c", 3));
List<SortedMap<Integer, Record>> recordsGroupedByName = groupRecordsByNameAndSortedByScoreDescending(records);
Collections.sort(recordsGroupedByName, byHighestScoreInGroupDescending());
List<Record> result = flattenGroups(recordsGroupedByName);
List<Record> expected = asList(
new Record("b", 10),
new Record("b", 7),
new Record("a", 9),
new Record("a", 3),
new Record("c", 8),
new Record("c", 3));
assertEquals(expected, result);
}
private List<Record> flattenGroups(List<SortedMap<Integer, Record>> recordGroups) {
List<Record> result = new ArrayList<Record>();
for (SortedMap<Integer, Record> group : recordGroups) {
result.addAll(group.values());
}
return result;
}
private List<SortedMap<Integer, Record>> groupRecordsByNameAndSortedByScoreDescending(List<Record> records) {
Map<String, SortedMap<Integer, Record>> groupsByName = new HashMap<String, SortedMap<Integer, Record>>();
for (Record record : records) {
SortedMap<Integer, Record> group = groupsByName.get(record.getName());
if (null == group) {
group = new TreeMap<Integer, Record>(descending());
groupsByName.put(record.getName(), group);
}
group.put(record.getScore(), record);
}
return new ArrayList<SortedMap<Integer, Record>>(groupsByName.values());
}
private DescendingSortComparator descending() {
return new DescendingSortComparator();
}
private ByFirstKeyDescending byHighestScoreInGroupDescending() {
return new ByFirstKeyDescending();
}
private static class ByFirstKeyDescending implements Comparator<SortedMap<Integer, Record>> {
public int compare(SortedMap<Integer, Record> o1, SortedMap<Integer, Record> o2) {
return o2.firstKey().compareTo(o1.firstKey());
}
}
private static class DescendingSortComparator implements Comparator<Comparable> {
public int compare(Comparable o1, Comparable o2) {
return o2.compareTo(o1);
}
}
}
GROUP BY name ORDER BY MAX(score), score DESC
的等效排序,而不是ORDER BY name, score
? - Christoffer HammarströmassertEquals
很简单。 - Christoffer Hammarström