Wordcounter,使用Lambdas和Fork / Join计算Java中的单词数
這些天來,我發(fā)布了Wordcounter ,這是一個Java庫和命令行實用程序,用于對文本文件中的單詞進行計數(shù)并對單詞計數(shù)進行分析,從而大量使用了功能編程結(jié)構(gòu)和并行計算方法。 這是我在“令人討厭的快速問答”大賽第四個條目SAP ,經(jīng)過給料機 , 托多爾和Hanoier 。
該庫使用JDK 8 lambda ,以及新的JDK 7功能,例如Fork / Join和NIO.2 。 它是內(nèi)置的,只能與支持lambda的JDK 8的早期訪問版本一起使用 。
隨著JDK 8中l(wèi)ambda及其支持功能的引入,我們用Java構(gòu)建軟件的方式將發(fā)生變化。 如果您想了解幾年后Java代碼的外觀,可以看看Wordcounter。 與當前大多數(shù)資源不同,這不是一個簡單的教程,而是一個實際的項目。
競賽任務要求使用Fork / Join和lambdas實現(xiàn)算法,該算法分析目錄中的所有文件,并找到文件中十個最常用的單詞以及它們出現(xiàn)的次數(shù)。 我沒有簡單地堅持使用Fork / Join,而是嘗試找到最適合此任務的并行方法,這使我選擇了Producer / Consumer作為核心的單詞計數(shù)邏輯。
您可以在github上探索源代碼。 還有一個相當全面的自述文件,它提供了更詳細的文檔。
最新的二進制,javadoc和源代碼包可在GitHub 下載部分中找到 。 如果有足夠的興趣,我將在線發(fā)布Javadoc,并在中央Maven存儲庫中提供該庫。
歡迎反饋,評論和貢獻!
總覽
圖書館特色
- 計算字符串,單個文本文件或包含文本文件的目錄樹中的所有單詞。
- 分析單詞計數(shù)以找到前N個最常用的單詞,后N個最不常用的單詞或總單詞數(shù)。
- 通過外部謂詞指定字符是否為文字字符。
- 指定要對單詞執(zhí)行的可選操作,例如通過外部運算符轉(zhuǎn)換為小寫字母。
- 在非并行和并行實現(xiàn)之間進行選擇,以比較其性能。
- 如果需要,將并行度指定為與內(nèi)核數(shù)不同的值。
編程要點
- 使用生產(chǎn)者/使用者來讀取文件并并行計算每個文件中的單詞。 實際的機制封裝在通用的可重用實現(xiàn)中。
- 使用Fork / Join對字數(shù)進行分析。 這里,實際的機制再次封裝在通用的可重用實現(xiàn)中。
- 使用NIO.2遍歷目錄樹和讀取文件。
- 大量使用函數(shù)接口和lambda表達式 ,以便在適當?shù)牡胤絺鬟f函數(shù)而不是數(shù)據(jù)。
- 有兩個最重要的類的綜合單元測試和性能測試。
- 像往常一樣,代碼干凈,結(jié)構(gòu)合理且易于閱讀。 格式,命名和注釋是統(tǒng)一且一致的。 適當?shù)厥褂昧嗣嫦驅(qū)ο蠛凸δ芫幊碳夹g已引起了很多關注。
命令行界面
要啟動命令行程序,請執(zhí)行以下命令:
java -jar wordcounter-1.0.4.jar <options>所有選項都有合理的默認值,因此都不是必需的。 對所有選項使用默認值會導致在當前目錄及其子目錄中找到前10個最常用的單詞。 指定非默認值允許指定不同的目錄,分析模式,單詞字符,單詞數(shù)和并行度,以及忽略大小寫或使用串行而不是并行計算,例如:
在“單詞”目錄中找到最常用的10個單詞-p words
在目錄“ wordsx”中查找前5個最不常用的單詞,并將數(shù)字視為單詞字符,忽略大小寫,并進行信息記錄-p wordsx -m bottom -d 1234567890 -i -n 5 -l info
有關命令行界面選項的更多信息,請參見自述文件中的命令行界面 。
設計
庫的設計將問題劃分為通用并行處理實用程序,封裝用于表示原始字數(shù)和排序字數(shù)的數(shù)據(jù)結(jié)構(gòu)的類,最后是使用前兩組功能執(zhí)行計數(shù)和分析的類。 實際上,所有這些類都大量使用功能接口的實例,以便允許對其通用行為進行特定的自定義。 這導致代碼中大量注入了lambda表達式和方法引用。 歡迎來到Java函數(shù)編程的世界!
通用并行處理實用程序
ForkJoinComputer類
ForkJoinComputer<T>類是通用的Fork / Join計算機。 它將初始大小除以2,直到達到指定的并行度或低于指定的閾值,然后使用指定的Computer<T>串行計算每個部分,然后使用指定的Merger<T>將所有計算的結(jié)果Merger<T> 。 此處,計算機和合并是定義如下的功能接口:
public interface Computer<T> {T compute(int lo, int hi); }public interface Merger<T> {T merge(T result1, T result2); }可以通過簡單地使用適當?shù)膌ambda實例化該類,然后調(diào)用其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執(zhí)行程序。 它啟動一個Producer<T1>任務和多個Mediator<T1, T2>和Consumer<T2>任務,它們的數(shù)量等于指定的并行度。 生產(chǎn)者將T1實例放入BlockingQueue<T1> 。 中介者從那里獲取這些實例,將其轉(zhuǎn)換為T2 ,并將其放入另一個類型為BlockingQueue<T2>阻塞隊列中。 最后,使用者從第二個阻塞隊列中獲取T2實例并對其進行處理。
這里, Producer, Consumer和Mediator是功能接口,定義如下:
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); }在上面的代碼中, Block是java.util.functions定義的標準函數(shù)。 傳遞給Producer和Mediator方法的塊將產(chǎn)生的數(shù)據(jù)放入相應的阻塞隊列中。
與ForkJoinComputer相似,可以通過在適當?shù)膌ambda上實例化該類然后調(diào)用其execute方法來使用此類。
數(shù)據(jù)結(jié)構(gòu)類
這些類封裝了用于表示原始和已排序字數(shù)的數(shù)據(jù)結(jié)構(gòu)。
- WordCounts類表示映射到其使用計數(shù)的單詞列表。
- TopWordCounts類表示映射到具有此類計數(shù)的所有單詞的單詞使用情況計數(shù)的排序列表。
字數(shù)統(tǒng)計和分析類
WordCounter類
WordCounter類提供了一種方法,用于以串行或并行方式對表示文件或目錄樹的Path單詞進行計數(shù)。 通過使用適當?shù)膌ambda實例化它,然后調(diào)用其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();并行實現(xiàn)使用ProducerConsumerExecutor<Path, String> 。 生產(chǎn)者只需遍歷目錄樹并產(chǎn)生Path實例。 中介者將文件讀入文本片段,而使用者則對每個文本片段中的單詞進行計數(shù)并將它們收集在單個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產(chǎn)生的字數(shù)進行分析的方法,例如查找前N個最常用的字。 也可以通過簡單地實例化它,然后調(diào)用其方法之一(例如findTop或total來使用它:
// Find the top 10 most used words in wc TopWordCounts twc = new WordCountAnalyzer(wc, true).findTop(10, (x, y) -> (y - x));Differentnet分析類型實現(xiàn)內(nèi)部Analysis<T>接口,該接口定義如下:
interface Analysis<T> {T compute(int lo, int hi);T merge(T r1, T r2); }由于以上兩種方法的簽名模仿了ForkJoinComputer使用的Computer和Merger功能接口,因此我們可以通過以下方式對所有分析類型使用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());} }有關庫設計的更多信息,請參見自述文件中的設計。
性能
我發(fā)現(xiàn)并行的Producer / Consumer字數(shù)統(tǒng)計實現(xiàn)很好地適應了不同數(shù)量的內(nèi)核和I / O速度。 它比串行實現(xiàn)要快得多。 與之不同的是,當使用不切實際的大量唯一單詞進行測試時,并行的Fork / Join分析實現(xiàn)僅比串行的快,并且程度適中。 由于唯一字的數(shù)量很少,因此實際上比串行字慢。
下表比較了單詞計數(shù)的性能,并在以下條件下找到了最佳分析:
- CPU AMD Phenom II X4 965 3.4 GHz(4核),4 GB RAM,Windows 7,JDK 8
- 默認選項:由字母字符組成的單詞,區(qū)分大小寫
- 默認并行度,等于內(nèi)核數(shù)
字數(shù)統(tǒng)計性能
| 序列號 | 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構(gòu)建并運行測試仍然可以正常工作。 根據(jù)此博客文章 ,應該可以對lambda使用IntelliJ IDEA 12 EAP內(nèi)部版本122.202或更高版本,但是我沒有親自嘗試過。 我確實嘗試了Eclipse,但由于Eclipse使用了自己的對lambda不了解的JDT編譯器,因此發(fā)現(xiàn)它是一場失敗的比賽。
結(jié)論
這是我第一次接觸Java函數(shù)編程。 盡管Java仍然不是Scala,但是與我以前的Java代碼相比,新的函數(shù)式編程結(jié)構(gòu)大大改變了我設計和實現(xiàn)Wordcounter的方式。 我發(fā)現(xiàn)這種新的編程風格是強大而富有表現(xiàn)力的,并且我相信隨著Java 8的發(fā)布,它將很快成為主流。
對我來說,這也是最后的“怪異敏捷”任務。 評審團慷慨地授予了我的意見書,甚至在比賽結(jié)束之前,我就找到了自己的優(yōu)勝者。
如果這篇文章引起了您的興趣,請隨時下載并瀏覽Wordcounter,用它來學習新的Java函數(shù)編程構(gòu)造,并讓我知道我是否可以在此過程中為您提供幫助。
參考: Wordcounter,來自JCG合作伙伴 Stoyan Rachev的Lambdas和Fork / Join在Java中計算單詞數(shù),網(wǎng)址 為Stoyan Rachev博客。
翻譯自: https://www.javacodegeeks.com/2012/12/wordcounter-counting-words-in-java-with-lambdas-and-forkjoin.html
總結(jié)
以上是生活随笔為你收集整理的Wordcounter,使用Lambdas和Fork / Join计算Java中的单词数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑系统在哪里关闭(电脑怎么关闭?)
- 下一篇: 国服第一打野剑圣符文配置(剑圣打野铭文)