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

这些天来,我发布了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>将所有计算的结果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

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

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

相关文章

BZOJ2659: [Beijing wc2012]算不出的算式

2659: [Beijing wc2012]算不出的算式 Time Limit: 3 Sec Memory Limit: 128 MBSubmit: 1489 Solved: 891[Submit][Status][Discuss]Description 算不出的算式 背景&#xff1a; 曾经有一个老掉牙的游戏放在我面前&#xff0c;我没有珍惜。直到这个游戏停产才追悔莫及。人世间…

Android Studio报Error:Execution failed for task #39;:Companion:preDexDebug#39;.

错误例如以下&#xff1a; Error:Execution failed for task :Companion:preDexDebug. > com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process command C:\Program Files\Java\jdk1.8.0_40\bin\java.exe finished with n…

go 修改结构体方法_「GCTT 出品」Go 系列教程——26. 结构体取代类

Go语言中文网&#xff0c;致力于每日分享编码、开源等知识&#xff0c;欢迎关注我&#xff0c;会有意想不到的收获&#xff01;Go 支持面向对象吗&#xff1f;Go 并不是完全面向对象的编程语言。Go 官网的 FAQ 回答了 Go 是否是面向对象语言&#xff0c;摘录如下。可以说是&…

WindowsLinux常用命令笔记

目录linux windows Linux: 1.查找文件 find / -name filename.txt 根据名称查找/目录下的filename.txt文件。 find . -name "*.xml" 递归查找所有的xml文件 find . -name "*.xml" |xargs grep "hello world" 递归查找所有文件内容中包含hello w…

button 和input 的区别及在表单form中的用法

先说一下button 和input的定义&#xff1a; <button> 标签定义的是一个按钮 1、在 <button> 元素内部&#xff0c;您可以放置任何内容&#xff0c;比如文本或图像。这是该元素与使用 <input> 元素创建的按钮之间的不同之处&#xff1b; 2、 <button> …

走向REST:在Spring和JAX-RS(Apache CXF)中嵌入Jetty

对于服务器核心Java开发人员而言&#xff0c;向世界“展示”的唯一方法是使用API​​。 今天的帖子都是关于JAX-RS的 &#xff1a;使用Java编写和公开RESTful服务。 但是&#xff0c;我们不会使用涉及应用程序服务器&#xff0c;WAR打包以及诸如此类的传统的重量级方法来做到这…

分组后分页_SQL(约束、视图、分页、序列、索引、同义词、创建用户,为用户授权、执行计划的使用 数据的导入导出)...

学习主题&#xff1a;SQL学习目标&#xff1a;掌握约束掌握视图修改表名与删除表删除表中的列语句的语法结构是什么&#xff1f;答&#xff1a;delete 表名from table where ;删除表中的列语句的语法结构是什么&#xff1f;答&#xff1a;delete 表名from table where ;截断表的…

第三百三十四节,web爬虫讲解2—Scrapy框架爬虫—Scrapy爬取百度新闻,爬取Ajax动态生成的信息...

第三百三十四节&#xff0c;web爬虫讲解2—Scrapy框架爬虫—Scrapy爬取百度新闻&#xff0c;爬取Ajax动态生成的信息 crapy爬取百度新闻&#xff0c;爬取Ajax动态生成的信息&#xff0c;抓取百度新闻首页的新闻rul地址 有多网站&#xff0c;当你浏览器访问时看到的信息&#xf…

BEM思想之彻底弄清BEM语法

BEM的意思就是块&#xff08;block&#xff09;、元素&#xff08;element&#xff09;、修饰符&#xff08;modifier&#xff09;,是由Yandex团队提出的一种前端命名方法论。这种巧妙的命名方法让你的CSS类对其他开发者来说更加透明而且更有意义。BEM命名约定更加严格&#xf…

Hibernate Search 4.2最终发布:支持空间查询

JBoss已宣布发布Hibernate Search 4.2 final。 您可以从Sourceforge下载它或使用Maven构件 。 在新版本中&#xff0c;包含了一些有趣的功能&#xff1a; Hibernate Search现在支持空间查询 。 使用Spatial扩展&#xff0c;您可以将全文查询与基于到空间点的距离的限制结合起…

推荐算法

5类系统推荐算法,非常好使,非常全 今日头条用了哪五种推荐算法 视频 今日头条核心技术“个性推荐算法”揭秘转载于:https://www.cnblogs.com/lhuser/p/8306092.html

python的应用包括哪些_Python应用领域有哪些?

1、数据分析与处理通常情况下&#xff0c;Python被用来做数据分析。用C设计一些底层的算法进行封装&#xff0c;然后用Python进行调用。因为算法模块较为固定&#xff0c;所以用Python直接进行调用&#xff0c;方便且灵活&#xff0c;可以根据数据分析与统计的需要灵活使用。Py…

win10系统同时安装python2和python3

1、官网下载python2和python3版本 2、安装python3&#xff0c;勾上Add Python3.5 to PATH&#xff0c;自定义选择安装目录&#xff0c;安装&#xff0c;验证&#xff1a;WINR--->cmd&#xff0c;输入python看看是否安装python3 3、安装python2&#xff0c;自定义安装目录&am…

使用NoSQLUnit测试Spring Data MongoDB应用程序

Spring Data MongoDB是Spring Data项目中的项目&#xff0c;它提供了Spring编程模型的扩展&#xff0c;用于编写使用MongoDB作为数据库的应用程序。 要使用NoSQLUnit为Spring Data MongoDB应用程序编写测试&#xff0c;除了考虑Spring Data MongoDB使用一个名为_class的特殊属…

20170117小测

今天再次迎来了我们的例行考试。 T1&#xff1a; 首先我们考虑那些点是可以共存的&#xff0c;我们可以枚举一个质数做他们的gcd&#xff0c;然后把这些点放在一张图里求直径。所以我们要做的就是把这些点的值分解质因数&#xff0c;对每个质因数挂一个链&#xff0c;代表有那些…

java实现红包要多少钱_java实现红包的分配算法

个人推测&#xff0c;微信红包在发出的时候已经分配好金额。比如一个10元的红包发给甲乙丙三个人&#xff0c;其实在红包发出去的时候&#xff0c;已经确定了第一个会领取多少&#xff0c;第二个会领取多少金额。而不是在领取的时候才计算的。下面贴出实现方法&#xff1a;publ…

iOS中Safari浏览器select下拉列表文字太长被截断的处理方法

网页中的select下拉列表&#xff0c;文字太长的话在iOS的Safari浏览器里会被自动截断&#xff0c;显示成下面这种&#xff1a; 安卓版的浏览器则没有这个问题。 如何让下拉列表中的文字在iOS的Safari浏览器里显示完整呢&#xff1f;答案是使用<optgroup></optgroup>…

HDU1042 N!(大整数类应用)

Problem Description Given an integer N(0 ≤ N ≤ 10000), your task is to calculate N!InputOne N in one line, process to the end of file.OutputFor each N, output N! in one line.Sample Input123Sample Output126Code&#xff1a;#include <iostream> #includ…

Java内部具有原子更新的动态热交换环境

有人可能会说上述标题可以简称为OSGi &#xff0c;我想在一开始就放弃这种思考过程。 对于OSGi而言&#xff0c;这不是一个冒犯&#xff0c;这是一个很棒的规范&#xff0c;在实现层或可用性层上都弄得一团糟&#xff0c;这就是我对OSGi的信念。 当然&#xff0c;您可以使用OS…

Css Secret 案例Demo全套

Css Secret 案例全套 github地址 案例地址 去年买了一本CSS揭秘的css专题书&#xff0c;该书揭示了 47 个鲜为人知的 CSS 技巧&#xff0c;主要内容包括背景与边框、形状、 视觉效果、字体排印、用户体验、结构与布局、过渡与动画等。去年买入时&#xff0c;就决定将里面所有…