对 Java 集合进行排序和分组

2024-04-20

我有一个有名称和分数的对象。我想对此类对象的集合进行排序,以便它们按名称分组并按每组中的最大分数排序(并且在组内也按降序分数排序)。

让我展示一下我想要实现的目标。假设我有这些对象(名称,分数):

(a, 3)
(a, 9)
(b, 7)
(b, 10)
(c, 8)
(c, 3)

然后我希望它们像这样排序:

(b, 10)
(b, 7)
(a, 9)
(a, 3)
(c, 8)
(c, 3)

这对于比较器可行吗?我无法弄清楚,所以任何提示将不胜感激。


不,你不能用单一排序来做到这一点Comparator.

你必须:

  1. 按名称分组
  2. 按组中最高分对组进行排序
  3. 那么你需要扁平化组返回列表。

使用 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());

记录被分组到已排序的位置TreeSets,而不是将值作为流的第一个操作进行排序,然后按其第一个最高值对集合进行排序。

如果组很大,则在排序之前进行分组是适当的,以减少冗余比较。


实施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);
        }
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

对 Java 集合进行排序和分组 的相关文章

随机推荐