Java、Scala、Python ☞ 本地WordCount詞頻統計對比

appleyk發表於2018-09-06

 

需求:模擬MapReduce,對磁碟檔案(N個)裡面的單詞進行詞頻統計(統計每個單詞在檔案中出現的次數)

區別:計算採用本地模式(單執行緒),只是模擬Map和Reduce的聯合過程,並不單獨分離出兩個任務(方法)

目的:通過不同語言實現詞頻統計功能,並對比各自的風格

 

 

一、資料樣例(Samples)

 

 

百度網盤:wordcount.rar

 

 

 

主要是為了測試,所以資料儘量怎麼簡單怎麼來,也可以自定義樣例資料

 

 

 

二、思路

 

 

 

 

 

三、程式設計實現

 

 

 

 

 

 

(1) Java demo實現

 

 

package com.appleyk.java;

import sun.applet.Main;

import java.io.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Description
 * @Author Appleyk
 * @Blob https://blog.csdn.net/appleyk
 * @Date Created on 上午 8:44 2018-9-6
 */
public class JWordCount {

    // main方法快捷鍵  :  psvm
    public static void main(String[] args) throws  Exception{

        System.out.println("================Java=============");
        String path = "F:\\wordcount";
        File dirFile = new File(path);
        // file陣列
        File[] files = dirFile.listFiles();
        for (File f : files) {
            System.out.println(f);
        }
        // file陣列轉List
        List<File> fileList = Arrays.asList(files);
        Map<String,Integer> wordMap = new HashMap<>();
        fileList.forEach(file->{
            try{
                BufferedReader bReader = new BufferedReader(new FileReader(file));
                // 迴圈讀取行
                String line = "";
                while((line=bReader.readLine())!=null){
                    String[] lines = line.split(" ");
                    for (String word : lines) {
                        if(wordMap.containsKey(word)){
                            int value = wordMap.get(word);
                            wordMap.put(word, value+=1);
                        }else{
                            wordMap.put(word, 1);
                        }
                    }
                }
                bReader.close();//釋放資源
            }catch (IOException ex){
                System.out.println(ex.getMessage());
            }
        });
        System.out.println("=====Java詞頻統計結果列印輸出======");
        System.out.println(wordMap);
        for(Map.Entry<String,Integer> result:wordMap.entrySet()){
            System.out.println("單詞:"+result.getKey()+",個數:"+result.getValue());
        }
    }
}

 

 

 

Java大家都是很熟悉了吧,因此寫起來demo毫不費勁,總體下來還是一氣呵成的,程式碼不做過多解釋,唯一需要說明的就是demo中採用了Java8的特性 -- forEach(很方便)

 

我們看下執行的結果:

 

================Java=============
F:\wordcount\1.txt
F:\wordcount\2.txt
F:\wordcount\3.txt
F:\wordcount\4.txt
=====Java詞頻統計結果列印輸出======
{a=8, b=8, c=12, d=8, e=8, f=4}
單詞:a,個數:8
單詞:b,個數:8
單詞:c,個數:12
單詞:d,個數:8
單詞:e,個數:8
單詞:f,個數:4

 

 

(2) Scala demo實現

 

 

package com.appleyk.scala

import java.io.File
import scala.io.Source

object SWordCount{
  def main(args: Array[String]): Unit = {

    System.out.println("================Scala=============")
    val path = "F:\\wordcount"     //目錄url
    val dirFile = new File(path)   //混搭Java程式碼,new一個file物件,該物件是一個目錄
    val files = dirFile.listFiles  //列出file目錄下的所有檔案【Java中是一個file陣列】

    //遍歷files物件【陣列】
    for(file <- files) println(file)

    // 將files物件轉化為List集合
    val fileList = files.toList

    // Scala中使用不可變的對映。如果想使用可變集,必須明確地匯入scala.collection.mutable.Map類
    // wordMap變數不可變,但是其指向的卻是一個可變的Map集合,鍵值對 --> key:String -> value:Int
    val wordMap = scala.collection.mutable.LinkedHashMap[String,Int]()

    // 遍歷【非常恐怖,三次foreach搞定詞頻統計!!!】
    fileList foreach(file=> Source.fromFile(file).getLines() //從file裡面讀取二進位制流,並獲取流的行資料列表
      .foreach(line=>line.split(" ") // 每一行資料根據空格分割成words列表
      .foreach(word=>{
           //判斷word【單詞】是否在map裡
           if(wordMap.contains(word)){
               wordMap(word)+=1      // 有的話,計數器(key對應的value)+1
           }else{
               wordMap+=(word -> 1)  // 否則的話,將單詞word加進map集合中,並初始化value=1
           }
      })
      ))
     printf("=====C編碼風格的輸出【詞頻統計結果列印】======%s","\n")
     System.out.println(wordMap)
     for((k,v) <- wordMap) printf("單詞:%s,個數:%d \n",k,v)
  }
}

 

 

       由於Scala執行於Java平臺(Java虛擬機器)上,併相容現有的Java程式,因此,我們在編碼的時候也可以混搭著Java的程式碼;同時,我們對比發現,Scala的編碼風格真的很隨意,可以【物件 空格 foreach】也可以使用【物件.foreach】的方式進行列表的遍歷,同時,其支援lambda表示式【匿名函式】,結合foreach,我們可以利用很少的程式碼將整個需求給實現出來,而且map集合的for迴圈列印出key-value鍵值對也是很輕鬆的,比較Java,Scala確實很棒,博主在學習Scala語言的時候,發現其和Python的語法有些像,所謂語言都是相通的,學好一門語言,其他語言基本上都能很快入手,不多說,我們看下Scala執行的效果:

 

 

================Scala=============
F:\wordcount\1.txt
F:\wordcount\2.txt
F:\wordcount\3.txt
F:\wordcount\4.txt
=====C編碼風格的輸出【詞頻統計結果列印】======
Map(a -> 8, b -> 8, c -> 12, d -> 8, e -> 8, f -> 4)
單詞:a,個數:8 
單詞:b,個數:8 
單詞:c,個數:12 
單詞:d,個數:8 
單詞:e,個數:8 
單詞:f,個數:4 

 

 

需要說明下,使用Map集合,最後的結果是亂序的,因此demo中使用了LinkedHashMap【有序Map】

 

 

(3) Python demo實現

 

 

#!/usr/bin/env Python3
# -*- encoding:utf-8 -*-

import  os   #匯入os模組,並使用該模組呼叫系統命令,獲得路徑,作業系統的型別等

if __name__=="__main__":

    print("================Pyhton=============")
    path="F:\\wordcount"      #目錄url
    filenames = os.listdir(path) #os.listdir() 方法用於返回指定的資料夾包含的檔案或資料夾的名字的列表,注意是名字

    #拿到檔案的全路徑名稱集合
    filefullnames = [os.path.join(path,filename) for filename in filenames]

    for name in filefullnames:
        print(name)

    #遍歷檔名列表+完整路徑拼接+生成檔案物件列表
    fileList = [open(filefullname) for filefullname in filefullnames]

    #上面的生成式的當時等價於下面的for程式碼塊
    # for filename in filenames:
    #     filename = os.path.join(path,filename)    #檔案完整路徑拼接
    #     fileList.append(open(filename,'r'))       #新增檔案物件


    wordMap = {} #定義一個單詞的字典【dict】集合

    #遍歷檔案物件列表
    for file in fileList:
        for line in file: #讀取檔案中的每一行
           words = line.strip('\n').split(" ") #消除換行符,並對每一行資料進行空格分割
           for word in words: #遍歷單詞列表
               if wordMap.get(word) is not None: #如果字典表裡麵包含了單詞word
                   wordMap[word]+=1  #出現頻率+1
               else:
                   wordMap[word]=1   #新增一個單詞,初始化頻率為1
        file.close() #釋放資源


    print("=====Python詞頻統計結果列印輸出======")
    print(wordMap)
    for key in wordMap.keys():
        print("單詞:%s,個數:%d" %(key,wordMap.get(key)))

 

 

        如果說Scala在對變數的型別沒有太大要求的話【val:不可變,var:可變,但是型別是自動匹配的】的話,那麼Python對變數簡直就是沒有要求,你不需要給變數指定什麼String型別、Int型別或者是指定變數是val可變的還是var不可變的,其變數的賦值操作就是變數宣告和定義的過程,因此,在宣告一個變數的時候,必須先賦值

       

        而且Python有列表生成式,可以很方便的根據條件生成我們最終想要的結果列表,如下:

        利用列表生成式再結合條件過濾,生成一個偶數序列

 

 

 

         demo中,我們利用列表生成式(List generation)的特性拿到了檔案的完整路徑列表,如下

 

 

 

 

更多生成式的用法,可以參考我的博文:Python3學習(9)--列表生成式(List generation)

 

 

.....................................................話不多說,我們直接執行demo,看一下pyhton的執行情況

 

 

================Pyhton=============
F:\wordcount\1.txt
F:\wordcount\2.txt
F:\wordcount\3.txt
F:\wordcount\4.txt
=====Python詞頻統計結果列印輸出======
{'a': 8, 'b': 8, 'c': 12, 'd': 8, 'e': 8, 'f': 4}
單詞:a,個數:8
單詞:b,個數:8
單詞:c,個數:12
單詞:d,個數:8
單詞:e,個數:8
單詞:f,個數:4

 

 

 

 

四、最後

 

 

 通過對比,我們可以看出,在程式碼簡潔程度上,Scala和Python那是旗鼓相當啊,但如果只能選一個的話,那非Pyhton莫屬了!

 而且,Python寫起demo來比較嗨皮,,Java則是中規中矩的老大哥,Scala由於是分散式計算框架Spark的實現語言,因此,

 Scala我們可以稍微瞭解一下,語言都是相通的,建議多掌握幾門輔助語言,這樣有利於日常工作的順利開展

 

相關文章