我在JDK 8中的Stream-Powered Collections Functionality中介绍了将JDK 8的Streams与Java集合一起使用的强大功能。 我没有在那篇文章中讨论groupingBy Collector 减少操作的使用,因此在这篇文章中解决了分组问题。
这篇博文中的示例将演示如何将集合支持的流与groupingBy
收集器组合在一起,以按提供的分类指定的组重新组织底层集合的数据。 这些示例基于我先前的文章JDK 8中的Stream-Powered Collections Functionality中描述的Movie
类和Set
of Movie
类。
下一个代码清单演示了如何使用简单的语句将提供的Movie
Set
分组为电影等级(关键字) Map
到具有该等级(值)的电影。 groupingBy
收集器将此Map
作为键类型(在这种情况下为MpaaRating
)的映射提供给要分组的对象类型List
(在此情况下为Movie
)。
/**
* Demonstrate use of JDK 8 streams and Collectors.groupingBy to
* group movies by their MPAA ratings.
*/
private static void demonstrateGroupingByRating()
{
final Map<MpaaRating, List<Movie>> moviesByRating =
movies.stream().collect(groupingBy(Movie::getMpaaRating));
out.println("Movies grouped by MPAA Rating: " + moviesByRating);
}
在刚刚显示的示例中(以及在本文后面的示例中),静态导入java.util.stream.Collectors.groupingBy
允许我不需要使用Collectors
类名来限制groupingBy
调用的范围。 这个简单的代码片段通过将电影的等级与返回的电影等级的Map
映射键映射到与每个等级相关的电影List
来将电影分组。 这是当提供的Movie
set与我先前引用的帖子中的相同时的输出示例。
根据MPAA分级的电影:{PG13 = [电影:盗梦空间(2010),Science_FICTION,PG13、13],R = [电影:肖申克的救赎(1994),DRAMA,R,1],PG = [电影:攻略失落的方舟(1981),动作,PG,31,电影:回到未来(1985),SCIENCE_FICTION,PG,49,电影:星球大战:第五集–帝国反击(1980),SCIENCE_FICTION,PG,12 ]}
刚刚展示的功能的一种特定用途是生成一个唯一键Map
,该键Map
到Collection
中的对象,并Map
到具有该键的Collection
对象。 例如,在需要通过map反复快速查找对象但在Set
或List
而不是在Map
提供感兴趣的对象时,这可能很有用。 假装电影具有唯一的标题(它们仅适用于我的小型电影),那么可以如下面的代码清单所示完成这些功能。
/**
* Demonstrate use of JDK 8 streams and Collectors.groupingBy to
* group movies by their title.
*/
private static void demonstrateGroupingByTitle()
{
final Map<String, List<Movie>> moviesByTitle =
movies.stream().collect(groupingBy(Movie::getTitle));
out.println("Movies grouped by title: " + moviesByTitle);
}
假设标题对于原始集合中的每个电影都是唯一的,则上面的代码将电影标题映射到仅包含该标题适用的电影的单元素List
。 任何希望按标题快速查找电影的客户端都可以调用moviesByTitle.get(String).get(0)
来获取与该标题相对应的完整Movie
对象。 接下来显示使用我的简单电影集进行此操作的输出。
按标题分组的电影:{The Shawshank Redemption = [电影:The Shawshank Redemption(1994),DRAMA,R,1],《星球大战:第五集–帝国反击》 = [电影:星球大战:第五集–帝国反击Back(1980),SCIENCE_FICTION,PG,12],Back to the Future = [电影:回到未来(1985),SCIENCE_FICTION,PG,49],《夺宝奇兵》 [Ravies of the Lost Ark( 1981),ACTION,PG,31],Inception = [电影:Inception(2010),Science_FICTION,PG13,13]}
可以通过两个不同的特征进行分组。 这允许将Collection
按一个特征分组,然后将这些组中的每个分组按第二个特征进行子分组。 例如,以下代码清单按等级然后按流派将电影分组。
/**
* Demonstrate use of JDK 8 streams and cascaded groupingBy
* to group movies by ratings and then by genres within ratings.
*/
private static void demonstrateGroupingByRatingAndGenre()
{
final Map<MpaaRating, Map<Genre, List<Movie>>> moviesByRatingAndGenre =
movies.stream().collect(groupingBy(Movie::getMpaaRating, groupingBy(Movie::getGenre)));
out.println("Movies by rating and genre: " + moviesByRatingAndGenre);
}
刚显示的代码列表首先按等级将基础电影分组,然后再次将每部电影与特定等级的分组进行分组,但是这次按类型进行分组。 换句话说,我们通过收视率和流派获得了两级电影。 接下来显示我的一组简单电影的输出。
按等级和流派的电影:{PG13 = {SCIENCE_FICTION = [电影:盗梦空间(2010),Science_FICTION,PG13,13]}},R = {DRAMA = [电影:肖申克的救赎(1994),DRAMA,R,1]} ,PG = {SCIENCE_FICTION = [电影:回到未来(1985),Science_FICTION,PG,49,电影:《星球大战:第五集–帝国反击》(1980),Science_FICTION,PG,12],ACTION = [电影:夺宝奇兵(1981),ACTION,PG,31]}}
通过groupingBy
收集器,可以轻松地将List
或Set
元素分组为映射,并以分组特征为键,并将属于List
中每个组的对象与该分组特征键相关联。 这提供了Map
所有优点,包括使用JDK 8中引入的一些方便的Map
方法。
翻译自: https://www.javacodegeeks.com/2015/03/jdk-8-streams-and-grouping.html