分组,采样和批处理– Java 8中的自定义收集器

在第一篇文章的后续部分,这次我们将编写一些更有用的自定义收集器:用于按给定的标准进行分组,采样输入,批处理和在固定大小的窗口上滑动。

分组(计数事件,直方图)

假设您有一些项目的集合,并且想要计算每个项目(相对于equals() )出现在此集合中的次数。 这可以使用Apache Commons Collections的CollectionUtils.getCardinalityMap()来实现。 此方法采用Iterable<T>并返回Map<T, Integer> ,计算每个项目出现在集合中的次数。 但是,有时我们不使用equals()而是按输入T的任意属性分组。 例如,说我们有一个Person对象列表,我们想计算男性与女性的数量(即Map<Sex, Integer> )或年龄分布。 有一个内置的收集器Collectors.groupingBy(Function<T, K> classifier) –但是,它从键返回一个映射到映射到该键的所有项。 看到:

import static java.util.stream.Collectors.groupingBy;//...final List<Person> people = //...
final Map<Sex, List<Person>> bySex = people.stream().collect(groupingBy(Person::getSex));

这很有价值,但是在我们的案例中,不必要地构建了两个List<Person> 。 我只想知道人数。 没有内置的这种收集器,但是我们可以用相当简单的方式来组成它:

import static java.util.stream.Collectors.counting;
import static java.util.stream.Collectors.groupingBy;//...final Map<Sex, Long> bySex = people.stream().collect(groupingBy(Person::getSex, HashMap::new, counting()));

这个重载版本的groupingBy()具有三个参数。 如前所述,第一个是键( 分类器 )功能。 第二个参数创建了一个新地图,我们很快就会看到它为什么有用的原因。 counting()是一个嵌套的收集器,它将所有具有相同性别的人合并在一起-在我们的例子中,当他们到达时就简单地对其计数。 能够选择地图的实现方式非常有用,例如在构建年龄直方图时。 我们想知道在给定年龄下有多少人-但年龄值应排序:

final TreeMap<Integer, Long> byAge = people.stream().collect(groupingBy(Person::getAge, TreeMap::new, counting()));byAge.forEach((age, count) ->System.out.println(age + ":\t" + count));

我们最终从年龄(排序)开始,使用TreeMap来计算具有该年龄的人数。

采样,批处理和滑动窗口

Scala中的IterableLike.sliding()方法允许通过固定大小的滑动窗口查看集合。 该窗口从开始处开始,并且在每次迭代中移动给定数量的项目。 Java 8中缺少的这种功能允许使用多种有用的运算符,例如计算移动平均值 ,将大集合分成批处理(与Guava中的Lists.partition()比较)或对每个第n个元素进行采样。 我们将为Java 8实现具有类似行为的收集器。 让我们从单元测试开始,它应该简要描述我们想要实现的目标:

import static com.nurkiewicz.CustomCollectors.sliding@Unroll
class CustomCollectorsSpec extends Specification {def "Sliding window of #input with size #size and step of 1 is #output"() {expect:input.stream().collect(sliding(size)) == outputwhere:input  | size | output[]     | 5    | [][1]    | 1    | [[1]][1, 2] | 1    | [[1], [2]][1, 2] | 2    | [[1, 2]][1, 2] | 3    | [[1, 2]]1..3   | 3    | [[1, 2, 3]]1..4   | 2    | [[1, 2], [2, 3], [3, 4]]1..4   | 3    | [[1, 2, 3], [2, 3, 4]]1..7   | 3    | [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7]]1..7   | 6    | [1..6, 2..7]}def "Sliding window of #input with size #size and no overlapping is #output"() {expect:input.stream().collect(sliding(size, size)) == outputwhere:input | size | output[]    | 5    | []1..3  | 2    | [[1, 2], [3]]1..4  | 4    | [1..4]1..4  | 5    | [1..4]1..7  | 3    | [1..3, 4..6, [7]]1..6  | 2    | [[1, 2], [3, 4], [5, 6]]}def "Sliding window of #input with size #size and some overlapping is #output"() {expect:input.stream().collect(sliding(size, 2)) == outputwhere:input | size | output[]    | 5    | []1..4  | 5    | [[1, 2, 3, 4]]1..7  | 3    | [1..3, 3..5, 5..7]1..6  | 4    | [1..4, 3..6]1..9  | 4    | [1..4, 3..6, 5..8, 7..9]1..10 | 4    | [1..4, 3..6, 5..8, 7..10]1..11 | 4    | [1..4, 3..6, 5..8, 7..10, 9..11]}def "Sliding window of #input with size #size and gap of #gap is #output"() {expect:input.stream().collect(sliding(size, size + gap)) == outputwhere:input | size | gap | output[]    | 5    | 1   | []1..9  | 4    | 2   | [1..4, 7..9]1..10 | 4    | 2   | [1..4, 7..10]1..11 | 4    | 2   | [1..4, 7..10]1..12 | 4    | 2   | [1..4, 7..10]1..13 | 4    | 2   | [1..4, 7..10, [13]]1..13 | 5    | 1   | [1..5, 7..11, [13]]1..12 | 5    | 3   | [1..5, 9..12]1..13 | 5    | 3   | [1..5, 9..13]}def "Sampling #input taking every #nth th element is #output"() {expect:input.stream().collect(sliding(1, nth)) == outputwhere:input  | nth | output[]     | 1   | [][]     | 5   | []1..3   | 5   | [[1]]1..6   | 2   | [[1], [3], [5]]1..10  | 5   | [[1], [6]]1..100 | 30  | [[1], [31], [61], [91]]}
}

在Spock中使用数据驱动的测试,我成功地立即编写了近40个测试用例,简洁地描述了所有需求。 我希望这些对您来说都是清楚的,即使您以前从未看过这种语法。 我已经假定存在方便的工厂方法:

public class CustomCollectors {public static <T> Collector<T, ?, List<List<T>>> sliding(int size) {return new SlidingCollector<>(size, 1);}public static <T> Collector<T, ?, List<List<T>>> sliding(int size, int step) {return new SlidingCollector<>(size, step);}}

收藏家接连收到物品的事实使工作变得更加困难。 当然,首先收集整个列表并在列表上滑动会比较容易,但是却很浪费。 让我们迭代构建结果。 我什至不假装通常可以并行执行此任务,因此我将使combiner()未实现:

public class SlidingCollector<T> implements Collector<T, List<List<T>>, List<List<T>>> {private final int size;private final int step;private final int window;private final Queue<T> buffer = new ArrayDeque<>();private int totalIn = 0;public SlidingCollector(int size, int step) {this.size = size;this.step = step;this.window = max(size, step);}@Overridepublic Supplier<List<List<T>>> supplier() {return ArrayList::new;}@Overridepublic BiConsumer<List<List<T>>, T> accumulator() {return (lists, t) -> {buffer.offer(t);++totalIn;if (buffer.size() == window) {dumpCurrent(lists);shiftBy(step);}};}@Overridepublic Function<List<List<T>>, List<List<T>>> finisher() {return lists -> {if (!buffer.isEmpty()) {final int totalOut = estimateTotalOut();if (totalOut > lists.size()) {dumpCurrent(lists);}}return lists;};}private int estimateTotalOut() {return max(0, (totalIn + step - size - 1) / step) + 1;}private void dumpCurrent(List<List<T>> lists) {final List<T> batch = buffer.stream().limit(size).collect(toList());lists.add(batch);}private void shiftBy(int by) {for (int i = 0; i < by; i++) {buffer.remove();}}@Overridepublic BinaryOperator<List<List<T>>> combiner() {return (l1, l2) -> {throw new UnsupportedOperationException("Combining not possible");};}@Overridepublic Set<Characteristics> characteristics() {return EnumSet.noneOf(Characteristics.class);}}

我花了很多时间来编写此实现,尤其是正确的finisher()所以请不要害怕。 关键部分是一个buffer ,它可以收集项目,直到可以形成一个滑动窗口为止。 然后丢弃“最旧”的物品,并step向前滑动窗口。 我对这种实现并不特别满意,但是测试正在通过。 sliding(N) (与sliding(N, 1)同义词)将允许计算N项目的移动平均值。 sliding(N, N)将输入分成大小为N批次。 sliding(1, N)获取第N个元素(样本)。 希望您会发现这个收藏家有用,喜欢!

翻译自: https://www.javacodegeeks.com/2014/07/grouping-sampling-and-batching-custom-collectors-in-java-8.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/362651.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

将G1内的SIM卡联系人导入到GMAIL的联系人中

将G1内的SIM卡联系人导入到GMAIL的联系人中 具体方法是&#xff1a;进入联系人——按下“MENU"键——导入联系人——按下“MENU"键——“全部导入”——“Contact type”下选择“Google"。这样你的SIM联系人就可以导入到你的设备中了&#xff08;联系人后会有“…

Dreamweaver Flash Photoshop网页设计综合应用 (智云科技) [iso] 1.86G​

全书共15章&#xff0c;主要包括网页制作基础、Dreamweaver CC网页制作、Photoshop CC网页图像设计、Flash CC网页动画设计以及综合案例实战5个部分。通过本书的学习&#xff0c;不仅能让读者学会三大软件的基本操作&#xff0c;而且本书中列举的实战案例&#xff0c;还可以让读…

element解决表格错位问题

https://www.jianshu.com/p/de922caf337c https://blog.csdn.net/qq_15385627/article/details/81700731 保留默认参数&#xff1a; https://segmentfault.com/q/1010000017831798/a-1020000017841050 解决element表格错位 /*解决表格错位&#xff0c;必须加在index.html或APP…

margin和text-align实现水平居中的区别

1.首先text-align只应用于内联块和内联元素 text-align影响的是元素中的文本内容的对其方式&#xff08;默认是left&#xff0c;设置为center时水平居中&#xff09; 所以&#xff0c;将text-align设置为center时&#xff0c;只有当应用于块级元素并且元素内容为文本时&#xf…

如何使用示例从Java中的类路径加载资源

Java中的类路径不仅用于加载.class文件&#xff0c;而且还可以用于加载资源&#xff0c;例如属性文件&#xff0c;图像&#xff0c;图标&#xff0c;缩略图或任何二进制内容。 Java提供了API来将这些资源读取为InputStream或URL。 假设您在项目的config文件夹中有一个属性文件 …

Ext 3.0 +ASP.NET2.0 可视化开发介绍

Ext Designer 总算出来了&#xff01;&#xff01;&#xff01;基于Web的应用开发终于可以可视化开发了&#xff0c;而且可以几乎不敲1行代码。 准备工具&#xff1a; &#xff08;1&#xff09;Ext Designer 1.0.2 &#xff08;2&#xff09;Visual Studio 2005 第一步&#x…

收集五款常用的HTML编辑软件

HTML&#xff08;HyperText Mark-up Language&#xff09;即超文本标记语言或超文本链接标示语言&#xff0c;是目前网络上应用最为广泛的语言&#xff0c;也是构成网页文档的主要语言。HTML文本是由HTML命令组成的描述性文本&#xff0c;HTML命令可以说明文字、图形、动画、声…

创建vue项目(一)搭建vue-cli、项目文件介绍、简单配置

记录一下 拉取项目时 npm run dev 报错 输入&#xff1a;npm rebuild node-sass 再重新 npm run dev 如果不能解决&#xff0c;请看这里 一、搭建vue-cli vue create 项目名称? Please pick a preset: > default (babel, eslint) //默认 > Manually select features …

JPA / Hibernate实体状态转换的初学者指南

介绍 Hibernate将开发人员的思维方式从SQL语句转移到实体状态转换。 一旦实体由Hibernate主动管理&#xff0c;所有更改将自动传播到数据库。 操作域模型实体&#xff08;及其关联&#xff09;比编写和维护SQL语句容易得多。 如果没有ORM工具&#xff0c;则添加新列需要修改所…

C# 接收邮件

C#没有内置收邮件的类&#xff0c;参考网络上的代码&#xff0c;针对POP3协议服务器使用 Jmail组件来收邮件&#xff0c;针对IMAP协议服务器使用LumiSoft.Net 。 另外&#xff0c;一般免费邮箱需要在邮箱设置中开启 POP3&#xff08;或IMAP&#xff09;、 SMTP服务才可以使用非…

js来监控复制粘贴

平时我们在复制网页上面代码到控制台调试时&#xff0c;有时会出现复制过来的代码后面加上了一下描述信息&#xff08;作者、版权等信息&#xff09;&#xff0c;每次需要删除才能运行&#xff0c;所以今天看看怎么能保证我们粘贴的代码不携带这些信息呢&#xff1f; (function…

创建vue项目(二)引入elementUi、axios、准备静态资源、封装组件(.vue,js代码等)

下载安装node -> vue-cli -> 配置路由 -> 引入elementUi -> 公共组件 一、引入elementUi 顺便一提axios使用说明 和axios在vue中使用 二、准备静态资源 三、封装.vue公共组件 四、封装.js公共组件 五、封装公共的js代码 六、封装全局的filter、directive等 一、引入…

Spring DI的配置使用

1、 依赖和依赖注入 传统应用程序设计中所说的依赖一般指“类之间的关系”&#xff0c;那先让我们复习一下类之间的关系&#xff1a; 泛化&#xff1a;表示类与类之间的继承关系、接口与接口之间的继承关系&#xff1b; 实现&#xff1a;表示类对接口的实现&#xff1b; 依赖&a…

休眠身份,序列和表(序列)生成器

介绍 在我以前的文章中&#xff0c;我谈到了不同的数据库标识符策略。 这篇文章将比较最常见的替代主要关键策略&#xff1a; 身份 序列 表&#xff08;序列&#xff09; 身份 IDENTITY类型&#xff08;包括在SQL&#xff1a;2003标准中&#xff09;受以下支持&#xff1a;…

flex pv3d 有用公式

实用公式 统领全书&#xff0c;我们已经有了各种运动和效果的公式。我已经提取出了最实用和最常用的公式、方程、以及代码的摘录&#xff0c;并将它们列在本章的最后。我认为将它们放到同一个地方应该对大家非常有帮助&#xff0c;因此我将这些我认为最需要的内容放到一起作为…

http https http2.0

一.http状态码 1xx&#xff08;信息性状态码&#xff0c;接受的请求正在处理&#xff09;2xx&#xff08;成功状态码&#xff0c;请求正常处理完毕&#xff09;200 OK204 No Content&#xff1a;请求成功但没有资源返回206 Partial Content&#xff1a;范围请求3xx&#xff08…

创建vue项目(三)路由跳转、反向代理、本地存储、状态管理

数据更新渲染&#xff0c;axios请求数据&#xff0c;配置环境 一、路由跳转 app.vue <template><div id"app"><keep-alive><router-view/></keep-alive></div> </template><script> export default {name: app }…

jinfo:JVM运行时配置的命令行浏览

在最近的几篇博客中&#xff08;特别是在对Java EE 7性能调优和优化以及WildFly性能调优的书的评论中&#xff09;&#xff0c;我引用了自己过去在某些Oracle JDK命令行工具上的博客文章。 令我震惊的是&#xff0c;我从来没有专门解决过漂亮的jinfo工具&#xff0c;这篇文章旨…

javascript---parseInt(08)或parseInt(09)转换返回0的解决办法

javascript parseInt函数使用率非常高&#xff0c;主要功能是将一个string转换为integer。有两个重载&#xff1a; parseInt(s);parseInt(s,radix)第一个方式不再多说&#xff0c;第二个方式&#xff0c;radix是s所基于的进制。范围为2-36&#xff08;不在此范围函数将返回NaN&…

创建vue项目(四)路由相关知识、路由守卫、插槽、打包小细节

一、路由相关点 1. 路由跳转传参以及接参 https://segmentfault.com/a/1190000012393587 方法一&#xff1a; &#xff08;1&#xff09; 参数配置&#xff1a; { path : xx/:参数变量,component:xx}(2) 使用 <router-link toxx/参数></router-link>(3) 传参 …