对java集合进行排序和分组

编程入门 行业动态 更新时间:2024-10-27 07:25:32
本文介绍了对java集合进行排序和分组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

我有一个有名字和分数的对象。我想对这些对象的集合进行排序,以便按名称对它们进行分组,并按每组中的最高分数进行排序(以及在组内按降序分数排序)。

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

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

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

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

对于比较器来说这是可行的吗?我无法理解,所以任何提示都会受到赞赏。

解决方案

不,你做不到使用单个比较器进行单一排序。

您必须按名称分组,并且按群组中的最高分对群组进行排序。

然后您需要将群组展平返回列表。

使用Java 8

编辑:由于我写了这个答案,Java 8已经问世,这简化了问题很多:

import java.util。*; import static java.util.Comparator。*; import static java.util.stream.Collectors。*;

List< ;记录> result = records.stream() .sorted(comparisonInt(Record :: getScore).reversed()) .collect(groupingBy(Record :: getName,LinkedHashMap :: new,toList())) .values()。stream() .flatMap(Collection :: stream) .collect(toList());

首先我们按分数排序,然后我们使用 LinkedHashMap进行分组/ code>,这将保留密钥的插入顺序,因此分数较高的密钥将首先出现。

如果组较小,则首先排序是正常的,所以不同组中的对象之间的冗余比较不会造成太大的伤害。

此外,使用此方法,还会保留重复项。

或者,如果您不关心保留重复项,您可以:

比较<记录和GT; highestScoreFirst = comparisonInt(Record :: getScore).reversed(); 列表<记录> result = records.stream() .collect(groupingBy(Record :: getName, toCollection(() - > new TreeSet<>(highestScoreFirst)))) .values( ).stream() .sorted(比较(SortedSet :: first,highestScoreFirst)) .flatMap(Collection :: stream) .collect(toList());

将记录分组为已排序的 TreeSet s,而不是将值排序为流的第一个操作,然后按照第一个最高值对这些集进行排序。

在排序之前进行分组是合适的群体很大,减少多余的比较。

实施可比较:

你可以通过让你的记录工具可比较来缩短它

公共类记录实现Comparable< Record> { @Override public int compareTo(Record other){ //最高返回-Integerpare(getScore(),other.getScore()); / *或等价:返回Integerpare(other.getScore(),getScore()); * / } ... }

List< Record> result = records.stream() .collect(groupingBy(Record :: getName,toCollection(TreeSet :: new))) .values()。stream() .sorted(比较(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; 公共类GroupSortTest { @Test public void testGroupSort(){ List< Record> records = asList( new Record(a,3), new Record(a,9), new Record(b,7),新记录(b,10),新记录(c,8),新记录(c,3)); 列表< SortedMap<整数,记录>> recordsGroupedByName = groupRecordsByNameAndSortedByScoreDescending(records); Collections.sort(recordsGroupedByName,byHighestScoreInGroupDescending()); 列表<记录> result = flattenGroups(recordsGroupedByName); 列表<记录> expected = asList( new Record(b,10), new Record(b,7), new Record(a,9),新记录(a,3),新记录(c,8),新记录(c,3)); assertEquals(预期,结果); } 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()); } 返回结果; } private List< SortedMap< Integer,Record>> groupRecordsByNameAndSortedByScoreDescending(List< Record> records){ Map< String,SortedMap< Integer,Record>> groupsByName = new HashMap< String,SortedMap< Integer,Record>>(); for(记录记录:记录){ SortedMap<整数,记录> group = groupsByName.get(record.getName()); if(null == group){ group = new TreeMap< Integer,Record>(descending()); groupsByName.put(record.getName(),group); } group.put(record.getScore(),record); } 返回新的ArrayList< SortedMap< Integer,Record>>(groupsByName.values()); } private DescendingSortComparator descending(){返回新的DescendingSortComparator(); } private ByFirstKeyDescending byHhesthestScoreInGroupDescending(){ return new ByFirstKeyDescending(); } 私有静态类ByFirstKeyDescending实现Comparator< SortedMap< Integer,Record>> { public int compare(SortedMap< Integer,Record> o1,SortedMap< Integer,Record> o2){ return o2.firstKey()。compareTo(o1.firstKey()); } } 私有静态类DescendingSortComparator实现Comparator< Comparable> { public int compare(Comparable o1,Comparable o2){ return o2pareTo(o1); } } }

I have an object which has a name and a score. I would like to sort a collection of such objects so that they are grouped by name and sorted by maximum score in each group (and within the group by descending score as well).

let me demonstrate what I intend to achieve. assume I have these objects(name, score):

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

then I would like them to be sorted like this:

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

is this feasible with a Comparator? I can't figure it out, so any hints would be appreciated.

解决方案

No, you can't do it with a single sort with a single Comparator.

You have to group by name, and sort the groups, by highest score in group.

Then you need to flatten the groups back to a list.

With Java 8

Edit: Since i wrote this answer, Java 8 has come out, which simplifies the problem a lot:

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

First we sort by score reversed, and then we group using a LinkedHashMap, which will preserve the insertion order for the keys, so keys with higher score will come first.

Sorting first is OK if the groups are small, so the redundant compares between objects in different groups don't hurt so much.

Also, with this method, duplicates are preserved.

Alternatively, if you don't care about preserving duplicates, you can:

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

Where the records are grouped into sorted TreeSets, instead of sorting the values as the first operation of the stream, and then the sets are sorted by their first, highest value.

Grouping before sorting is appropriate if the groups are big, to cut down on redundant compares.

Implementing Comparable:

And you can make it shorter by having your record implement Comparable

public class Record implements Comparable<Record> { @Override public int compareTo(Record other) { // Highest first return -Integerpare(getScore(), other.getScore()); /* Or equivalently: return Integerpare(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());

Before Java 8

Edit: Here is a really rough unit test that demonstrates one way to do it. I haven't cleaned it up as much as i would have liked.

Stuff like this is painful in Java, and i would normally use Google Guava for this.

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()pareTo(o1.firstKey()); } } private static class DescendingSortComparator implements Comparator<Comparable> { public int compare(Comparable o1, Comparable o2) { return o2pareTo(o1); } } }

更多推荐

对java集合进行排序和分组

本文发布于:2023-11-25 16:29:31,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1630496.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:java

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!