这些天来,我发布了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

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

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

    lambdas for 这些天来,我发布了Wordcounter ,这是一个Java库和命令行实用程序,用于对文本文件中的单词进行计数并对单词计数进行分析,从而大量使用了功能编程结构和并行计算方法. ...

  2. java读取文本单词_使用Java计算文本文件中的单词数

    我们可以使用Java的BufferedReader类读取文件中的单词,然后根据空格字符拆分读取的数据.请参阅以下示例: 示例 考虑类路径中的以下文本文件. test.txtThis is Line 1 ...

  3. 笔记:Java中的单例设计模式

    之前接触过单例模式,当初不明白这样的设计用意,今天特地研究了下java中的单例设计模式的用处及用法. 单例模式:单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例类的特殊类.一个类 ...

  4. java 文件中查找指定的单词,查找指定文本文件中的单词数的Java程序

    查找指定文本文件中的单词数的Java程序 1 说明 在此程序中,我们需要找到给定文本文件中出现次数最多的单词.这可以通过使用文件指针以读取模式打开文件来完成.逐行读取文件.一次分割一行并存储在数组中. ...

  5. fork/join框架Java

    Fork/Join框架是Java7提供了的一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架. Fork/Join使用两个类: ForkJoi ...

  6. fork join框架_Java中的Fork / Join框架的简要概述

    fork join框架 Fork / Join框架是使用并发分治法解决问题的框架. 引入它们是为了补充现有的并发API. 在介绍它们之前,现有的ExecutorService实现是运行异步任务的流行选 ...

  7. java 金额_货币金额的计算 - Java中的BigDecimal

    java中数字的计算事件很烦,也很容易出错的地方,比如网上找来的这样的例子 public class Test { public static void main(String[] args) { S ...

  8. 【单例】JAVA中的单例

    单例模式,属于创建类型的一种常用的软件设计模式.通过单例模式的方法创建的类在当前进程中只有一个实例(根据需要,也有可能一个线程中属于单例,如:仅线程上下文内使用同一个实例. Java中单例模式定义:& ...

  9. java中的单例_细说Java中的几种单例模式

    在Java中,单例模式分为很多种,本人所了解的单例模式有以下几种,如有不全还请大家留言指点: 饿汉式 懒汉式/Double check(双重检索) 静态内部类 枚举单例 一.饿汉式 image 饿汉式 ...

最新文章

  1. Linux系统的启动引导过程
  2. 批量更改Windows操作系统文件名
  3. 使用快速傅里叶变换计算大整数乘法-代码
  4. 从零开始搭建自己的VueJS2.0+ElementUI单页面网站(一、环境搭建)
  5. 从zip中读取文件 合并到指定的文件中
  6. 10 岁研究计算机,电脑神童“不务正业”的技术路
  7. k8s-service定义文件的各属性说明
  8. java写龟兔赛跑_有关JAVA编写龟兔赛跑的游戏的问题。求助……
  9. Android移动端测试——adb、monkey
  10. 九大背包问题专题--二维费用的背包问题
  11. HDU4035 Maze 【树形DP】【期望DP】
  12. 基于nodejs的模拟数据分发服务
  13. Jspx.net Framework 6.38发布
  14. 软件工程(成本/效益分析)
  15. C# MES系统结构梳理
  16. 【解决方法】iOS 开发小技巧(一)
  17. [BUUCTF]PWN——[BJDCTF 2nd]snake_dyn
  18. 计算机上瘾英语对话,对手机上瘾的英语作文(精选5篇)
  19. win7 下搭建基于jdk1.7 android开发环境
  20. 分享16个经典面试问题回答思路

热门文章

  1. finally块不被执行的情况总结
  2. cassandra数据备份_Cassandra中的数据建模
  3. netflix 模式创新_创新设计模式:工厂模式
  4. uaa 授权_使用UAA OAuth2授权服务器–客户端和资源
  5. 逻辑建模与物理建模_架构层和建模域逻辑
  6. red hat安装宝塔_只需几分钟即可安装Red Hat Container Development Kit(视频)
  7. 垃圾收集算法,垃圾收集器_确定活动的热点垃圾收集器
  8. drools6.5_Drools 6.4.0.Final提供
  9. rx.observable_使用Java 8 CompletableFuture和Rx-Java Observable
  10. apache mesos_Apache Mesos + Marathon和Java EE