不,你不能用单一排序来做到这一点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
s,而不是将值作为流的第一个操作进行排序,然后按其第一个最高值对集合进行排序。
如果组很大,则在排序之前进行分组是适当的,以减少冗余比较。
实施Comparable
:
你可以通过记录实现来缩短时间Comparable
public class Record implements Comparable<Record> {
@Override
public int compareTo(Record other) {
// Highest first
return -Integer.compare(getScore(), other.getScore());
/* Or equivalently:
return Integer.compare(other.getScore(), 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中是痛苦的,我通常会使用谷歌番石榴 http://code.google.com/p/guava-libraries/为了这。
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);
}
}
}