lambdas for_Wordcounter,使用Lambdas和Fork / Join计算Java中的单词数

lambdas for

这些天来,我发布了Wordcounter ,这是一个Java库和命令行实用程序,用于对文本文件中的单词进行计数并对单词计数进行分析,从而大量使用了功能编程结构和并行计算方法。 这是我在“令人讨厌的快速问答”大赛第四个条目SAP ,经过给料机 , 托多尔和Hanoier 。

该库使用JDK 8 lambda ,以及新的JDK 7功能,例如Fork / Join和NIO.2 。 它是内置的,只能与支持lambda的JDK 8的早期访问版本一起使用 。

随着JDK 8中lambda及其支持功能的引入,我们用Java构建软件的方式将发生变化。 如果您想了解几年后Java代码的外观,可以看看Wordcounter。 与当前大多数可用资源不同,这不是简单的教程,而是一个实际的项目。

竞赛任务要求使用Fork / Join和lambdas实现算法,该算法分析目录中的所有文件并查找文件中十个最常用的单词以及它们出现的次数。 我没有简单地坚持使用Fork / Join,而是尝试找到最适合此任务的并行方法,这使我选择了Producer / Consumer作为核心的单词计数逻辑。

您可以在github上探索源代码。 还有一个相当全面的自述文件,提供了更详细的文档。

最新的二进制,javadoc和源代码包可在GitHub 下载部分中找到 。 如果有足够的兴趣,我将在线发布Javadoc,并在中央Maven存储库中提供该库。

欢迎提供反馈,评论和贡献!

总览

图书馆特色

  • 计算字符串,单个文本文件或包含文本文件的目录树中的所有单词。
  • 分析单词计数以找到前N个最常用的单词,后N个最不常用的单词或总单词数。
  • 通过外部谓词指定字符是否为文字字符。
  • 指定要对单词执行的可选操作,例如通过外部运算符转换为小写字母。
  • 在非并行和并行实现之间进行选择,以比较其性能。
  • 如果需要,将并行度指定为与内核数不同的值。

编程要点

  • 使用生产者/使用者来读取文件并并行计算每个文件中的单词。 实际的机制封装在通用的可重用实现中。
  • 使用Fork / Join对字数进行分析。 这里,实际的机制再次封装在通用的可重用实现中。
  • 使用NIO.2遍历目录树和读取文件。
  • 大量使用函数接口和lambda表达式 ,以便在适当的地方传递函数而不是数据。
  • 有两个最重要的类的综合单元测试和性能测试。
  • 像往常一样,该代码干净,结构合理且易于阅读。 格式,命名和注释是统一且一致的。 适当地使用了面向对象和功能编程技术已经引起了很多关注。

命令行界面

要启动命令行程序,请执行以下命令:

java -jar wordcounter-1.0.4.jar <options>

所有选项都有合理的默认值,因此都不是必需的。 对所有选项使用默认值会导致在当前目录及其子目录中找到前10个最常用的单词。 指定非默认值允许指定不同的目录,分析模式,单词字符,单词数和并行度,以及忽略大小写或使用串行而不是并行计算,例如:

在“单词”目录中找到最常用的10个单词-p words
在目录“ wordsx”中查找前5个最不常用的单词,并将数字视为单词字符,忽略大小写,并进行信息记录-p wordsx -m bottom -d 1234567890 -i -n 5 -l info

有关命令行界面选项的更多信息,请参见自述文件中的命令行界面 。

设计

库的设计将问题划分为通用并行处理实用程序,封装用于表示原始字数和排序字数的数据结构的类,最后是使用前两组功能执行计数和分析的类。 实际上,所有这些类都大量使用功能接口的实例,以便允许对其通用行为进行特定的自定义。 这导致代码中大量注入了lambda表达式和方法引用。 欢迎来到Java函数编程的世界!

通用并行处理实用程序

ForkJoinComputer类

ForkJoinComputer<T>类是通用的Fork / Join计算机。 它将初始大小除以2,直到达到指定的并行度或低于指定的阈值,然后使用指定的Computer<T>串行计算每个部分,然后使用指定的Merger<T>所有计算的结果。 在这里,计算机和合并是定义如下的功能接口:

public interface Computer<T> {T compute(int lo, int hi);
}public interface Merger<T> {T merge(T result1, T result2);
}

可以通过简单地使用适当的lambda实例化该类,然后调用其compute方法来使用此类。

new ForkJoinComputer<Integer>(n, 1000,(lo, hi) -> { int sum = 0; for (int i = lo + 1; i <= hi; i++) sum += i; return sum; },(a, b) -> a + b).compute();

ProducerConsumerExecutor类

ProducerConsumerExecutor<T1, T2>类是通用的Producer / Consumer执行程序。 它启动一个Producer<T1>任务和多个Mediator<T1, T2>Consumer<T2>任务,它们的数量等于指定的并行度。 生产者将T1实例放入BlockingQueue<T1> 。 中介者从那里获取这些实例,将其转换为T2 ,并将其放入另一个类型为BlockingQueue<T2>阻塞队列中。 最后,使用者从第二个阻塞队列中获取T2实例并对其进行处理。

在这里, Producer, ConsumerMediator是定义如下的功能接口:

public interface Producer<T> {void produce(Block<T> block);
}public interface Consumer<T> {void consume(T t);
}public interface Mediator<T1, T2> {void mediate(T1 t, Block<T2> block);
}

在上面的代码中, Blockjava.util.functions定义的标准函数。 传递给ProducerMediator方法的块将产生的数据放入相应的阻塞队列中。

ForkJoinComputer相似,可以通过简单地使用适当的lambda实例化该类,然后调用其execute方法来使用此类。

数据结构类

这些类封装了用于表示原始和已排序字数的数据结构。

  • WordCounts类表示映射到其用法计数的单词列表。
  • TopWordCounts类表示映射到所有具有此类计数的单词的单词使用情况计数的排序列表。

字数统计和分析类

WordCounter类

WordCounter类提供了一种方法,用于以串行或并行方式对表示文件或目录树的Path单词进行计数。 只需使用适当的lambda实例化它,然后调用其count方法即可使用它:

// Count all words consisting of only alphabetic chars, ignoring case, using parallel processing
WordCounts wc = new WordCounter(path, (c) -> Character.isAlphabetic(c), (s) -> s.toLowerCase(), true).count();

并行实现使用ProducerConsumerExecutor<Path, String> 。 生产者只需遍历目录树并产生Path实例。 中介者将文件读入文本片段,使用者将每个文本片段中的单词计数,然后将它们收集到单个WordCounts实例中。 这是通过以下代码完成的:

private WordCounts countPar() {final WordCounts wc = new WordCounts(parLevel);new ProducerConsumerExecutor<Path, String>((block) -> collectPaths(block),(file, block) -> readFileToBlock(file, block),(text) -> wc.add(countWords(text, pred, op)), parLevel).execute();return wc;
}

WordCountAnalyzer类

WordCountAnalyzer类提供了对WordCounter产生的字数进行分析的方法,例如查找前N个最常用的字。 也可以通过简单地实例化它,然后调用它的方法之一(例如findToptotal来使用它:

// Find the top 10 most used words in wc
TopWordCounts twc = new WordCountAnalyzer(wc, true).findTop(10, (x, y) -> (y - x));

Differentnet分析类型实现内部Analysis<T>接口,该接口定义如下:

interface Analysis<T> {T compute(int lo, int hi);T merge(T r1, T r2);
}

由于以上两种方法的签名模仿了ForkJoinComputer使用的ComputerMerger功能接口,因此我们可以通过以下方式对所有分析类型使用fork / join:

public TopWordCounts findTop(int number, Comparator<Integer> comparator) {return analyse(new FindTopAnalysis(number, comparator));
}private <T> T analyse(Analysis<T> a) {if (par) {return new ForkJoinComputer<T>(wc.getSize(), THRESHOLD, a::compute, a::merge, parLevel).compute();} else {return a.compute(0, wc.getSize());}
}

有关库设计的更多信息,请参见自述文件中的设计。

性能

我发现并行的Producer / Consumer字数统计实现很好地适应了不同数量的内核和I / O速度。 它比串行实现要快得多。 与之不同的是,当使用不切实际的大量唯一单词进行测试时,并行的Fork / Join分析实现仅比串行的实现快,而且程度适中。 由于唯一字的数量很少,因此实际上比串行字慢。

下表比较了单词计数的性能,并在以下情况下找到了最佳分析:

  • CPU AMD Phenom II X4 965 3.4 GHz(4核),4 GB RAM,Windows 7,JDK 8
  • 默认选项:由字母字符组成的单词,区分大小写
  • 默认并行度,等于内核数

字数统计性能

实作 档案 大小(MB) 时间(毫秒)
序列号 1个 10000000 〜65 2200-2400
平行 1个 10000000 〜65 500-600
序列号 100 10000000 〜65 1600-1800
平行 100 10000000 〜65 500-600

查找最佳分析性能

实作 最大数量 最佳 时间(毫秒)
序列号 2000000 10000000 10 200-250
平行 2000000 10000000 10 200-250

玩代码

如果您想使用这些代码,我建议您使用最新的NetBeans 7.3 beta,在撰写本文时为NetBeans IDE 7.3 Beta 2 。 请注意,即使在此版本中,也无法在IDE中编译lambda,因此周围到处都有红色标记。 但是,从IDE启动Maven构建并运行测试仍然可以正常工作。 根据此博客文章 ,应该可以对lambda使用IntelliJ IDEA 12 EAP内部版本122.202或更高版本,但是我没有亲自尝试过。 我确实尝试了Eclipse,但由于Eclipse使用了自己的对lambda无知的JDT编译器,因此发现它是一场失败的比赛。

结论

这是我第一次接触Java函数编程。 尽管Java仍然不是Scala,但是与我以前的Java代码相比,新的函数式编程结构大大改变了我设计和实现Wordcounter的方式。 我发现这种新的编程风格功能强大且富有表现力,我相信随着Java 8的发布,它将很快成为主流。

对我来说,这也是最后的“怪异敏捷”任务。 评审团慷慨地授予了我的意见书,甚至在比赛结束之前,我就找到了自己的优胜者。

如果这篇文章引起了您的兴趣,请随时下载并探索Wordcounter,用它来学习新的Java函数编程构造,并让我知道在这个过程中是否可以帮助您。

参考: Wordcounter,来自JCG合作伙伴 Stoyan Rachev的Lambdas和Fork / Join中的Java单词计数在 Stoyan Rachev博客中。

翻译自: https://www.javacodegeeks.com/2012/12/wordcounter-counting-words-in-java-with-lambdas-and-forkjoin.html

lambdas for

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

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

相关文章

Bitmap Font生成

工具&#xff1a;AngelCode 的 Bitmap Font Generator。把需要用到的文字写到一个txt&#xff0c;注意编码为Unicode。接着&#xff0c;在工具里 Edit->select chars from file选择刚才新建的txt文件。然后。。。之后的没啥细节&#xff0c;随意搞吧。转载于:https://www.cn…

分隔和截断字符串, boost string algorithm library中的split和trim

http://www.boost.org/doc/libs/1_46_1/doc/html/string_algo.html 这个库是个 headers only library  这个库提供了STL没有提供的 string-related算法, 但是实现做到了可以用在任何 character 的 container上 split 在写在线状态的改造时候要把一个字符串中描述的几种类型拆…

使用Spring Integration重试RabbitMQ

我最近阅读了有关使用RabbitMQ重试的方法 在这里&#xff0c;并想尝试类似的方法 Spring Integration &#xff0c;提供了一组很棒的集成抽象。 TL; DR解决的问题是重试一次消息&#xff08;在处理失败的情况下&#xff09;&#xff0c;两次重试之间有较大的延迟&#xff08…

Vue_(Router路由)-vue-router路由的基本用法

vue-router官网&#xff1a;传送门 vue-router起步&#xff1a;传送门 vue-router路由&#xff1a;Vue.js官网推出的路由管理器&#xff0c;方便的构建单页应用 单页应用&#xff1a;Single Page Application简称SPA&#xff0c;只有一个web页面的应用&#xff0c;用户与应用交…

利用boost做string到wstring转换,以及字符集转换

#include <boost/locale.hpp> int _tmain(int argc, _TCHAR* argv[]) {//std::locale::global(std::locale("utf-8"));std::locale::global(std::locale("")); // 设置全局的C运行库locale 可以针对cout fstream等单独设置 空表示默认使用当前系统…

P4198 楼房重建

[Luogu4198] 原题解 19.3.21 用线段树维护有关单调栈的问题 不要pushdown , 但是pushup的时候需要特别注意. 19.3.31 这里的\(pushup2\)其实就是几个特判 : 没有 , 直接返回当前区间答案 , 区间长度为\(1\) , 以及剩下两大类 , 这里有一个模板 : if(mx[ls]<tmp) return push…

Linux多线程实践(1) --线程理论

线程概念 在一个程序里的一个执行路线就叫做线程&#xff08;thread&#xff09;。更准确的定义是&#xff1a;线程是“一个进程内部的控制序列/指令序列”; 一切进程至少有一个执行线程; 进程 VS. 线程 1.进程是资源分配(进程需要参与资源的竞争)的基本单位,而线程是处理器调…

蓝桥杯 密文搜索(全排列)

题目描述福尔摩斯从X星收到一份资料&#xff0c;全部是小写字母组成。他的助手提供了另一份资料&#xff1a;许多长度为8的密码列表。福尔摩斯发现&#xff0c;这些密码是被打乱后隐藏在先前那份资料中的。请你编写一个程序&#xff0c;从第一份资料中搜索可能隐藏密码的位置。…

openshift_为Openshift + MongoDb应用程序编写验收测试

openshift验收测试用于确定是否满足规范要求。 它应在与生产环境尽可能相似的环境中运行。 因此&#xff0c;如果您的应用程序已部署到Openshift中&#xff0c;则您将需要一个与生产环境中使用的帐户平行的帐户&#xff0c;以运行测试。 在这篇文章中&#xff0c;我们将为部署到…

学 Win32 汇编[28] - 跳转指令: JMP、JECXZ、JA、JB、JG、JL、JE、JZ、JS、JC、JO、JP 等

跳转指令分三类:一、无条件跳转: JMP;二、根据 CX、ECX 寄存器的值跳转: JCXZ(CX 为 0 则跳转)、JECXZ(ECX 为 0 则跳转);三、根据 EFLAGS 寄存器的标志位跳转, 这个太多了.根据标志位跳转的指令: JE ;等于则跳转 JNE ;不等于则跳转JZ ;为 0 则跳转 JNZ ;不为 0 则跳转JS…

广告行业一些常用物料的尺寸

10-13 14:13 设计 /淘宝 海报 50cm 70cm &#xff08;宽 高&#xff09; 57cm 84cm &#xff08;宽 高&#xff09; 横幅 横幅尺寸高度默认为整10为单位&#xff0c;50、60、70、长度视环境而定&#xff0c;材料一般为牛津布&#xff0c;旗帜布&#xff0c;颜色有双色有彩…

Spring Security和自定义密码编码

在上一篇文章中&#xff0c;我们使用jdbc和md5密码编码将密码编码添加到了我们的spring安全配置中。 但是&#xff0c;在定制UserDetailsS​​ervices的情况下&#xff0c;我们需要对安全配置进行一些调整。 我们需要创建一个DaoAuthenticationProvider bean&#xff0c;并将…

智能变电站协议系列-2、SV/SMV协议示例(IEC61850)以及5G专网下的电力方案分析

文章目录 一、前言二、资料准备三、libiec61850的SV运行示例及抓包分析1、单独编译示例程序2、运行示例程序及5G专网场景下部署3、wireshark抓包分析 四、最后 一、前言 之前我们对IEC61850协议有了整体的了解&#xff0c;对一些概念有了一定的认识&#xff0c;并针对GOOSE协议…

php 常用的知识点归集(下)

24、静态属性与静态方法在类中的使用 需求&#xff1a;在玩CS的时候不停有伙伴加入&#xff0c;那么现在想知道共有多少人在玩&#xff0c;这个时候就可能用静态变量的方法来处理 利用原有的全局变量的方法来解决以上的问题 <?php header(content-type:text/html;charsetut…

GDB下查看内存命令(x命令)

可以使用examine命令(简写是x)来查看内存地址中的值。x命令的语法如下所示&#xff1a; x/<n/f/u> <addr> n、f、u是可选的参数。 n是一个正整数&#xff0c;表示需要显示的内存单元的个数&#xff0c;也就是说从当前地址向后显示几个内存单元的内容&#xff0c;一…

[19/03/21-星期四] 异常(Exception) (一)

一、引言 在实际工作中&#xff0c;我们遇到的情况不可能是非常完美的。比如&#xff1a;你写的某个模块&#xff0c;用户输入不一定符合你的要求;你的程序要打开某个文件&#xff0c; 这个文件可能不存在或者文件格式不对 &#xff0c;你要读取数据库的数据&#xff0c;数据可…

jax-rs jax-ws_JAX-RS Bean验证错误消息国际化

jax-rs jax-wsBean验证简介 JavaBeans验证&#xff08;Bean验证&#xff09;是Java EE 6平台的一部分提供的新验证模型。 约束通过以JavaBeans组件&#xff08;例如托管Bean&#xff09;的字段&#xff0c;方法或类上的注释形式的约束来支持Bean验证模型。 javax.validation.c…

补码和原码的转化过程

在计算机系统中&#xff0c;数值一律用补码来表示&#xff08;存储&#xff09;。 主要原因&#xff1a; 使用补码&#xff0c;可以将符号位和其它位统一处理&#xff1b;同时&#xff0c;减法也可按加法来处理。另外&#xff0c;两个用补 码表示的数相加时&#xff0c;如果最高…

Flask实现群聊

后端 from geventwebsocket.handler import WebSocketHandler from gevent.pywsgi import WSGIServer from geventwebsocket.websocket import WebSocket from flask import Flask,request,render_template user_socket_list [] app Flask(__name__)app.route("/conn_ws…

第一单元总结:基于基础语言、继承和接口的简单OOP

前情提要 到目前为止&#xff0c;OO课程已经完成了前三次的作业&#xff0c;分别为&#xff1a; 第一次作业&#xff1a;简单多项式的构造和求导。【正则表达式】【数据结构】【排序】第二次作业&#xff1a;含三角函数因子的复杂多项式的构造、求导和化简。【递归下降】【DFS】…