Java nio 學習筆記(四) 淘寶2012校招技術筆試題
阿新 • • 發佈:2019-01-07
實現五:統計一個單詞可重複的英文檔案(假設4G)中每個單詞出現的次數,把結果按照英文排序放入一個檔案中。並能夠檢索特定單詞的出現次數。由於檔案過大,不重複單詞總數有限,需要考慮到執行速度和記憶體使用情況。(淘寶筆試技術題)
import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.charset.Charset; import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; import java.util.TreeMap; public class TestCountWords { public static void main(String[] args) { File wf = new File("words.txt"); final CountWords cw1 = new CountWords(wf, 0, wf.length()/2); final CountWords cw2 = new CountWords(wf, wf.length()/2, wf.length()); final Thread t1 = new Thread(cw1); final Thread t2 = new Thread(cw2); //開闢兩個執行緒分別處理檔案的不同片段 t1.start(); t2.start(); Thread t = new Thread() { public void run() { while(true) { //兩個執行緒均執行結束 if(Thread.State.TERMINATED==t1.getState() && Thread.State.TERMINATED==t2.getState()) { //獲取各自處理的結果 HashMap<String, Integer> hMap1 = cw1.getResult(); HashMap<String, Integer> hMap2 = cw2.getResult(); //使用TreeMap保證結果有序 TreeMap<String, Integer> tMap = new TreeMap<String, Integer>(); //對不同執行緒處理的結果進行整合 tMap.putAll(hMap1); tMap.putAll(hMap2); //列印輸出,檢視結果 for(Map.Entry<String,Integer> entry : tMap.entrySet()) { String key = entry.getKey(); int value = entry.getValue(); System.out.println(key+":\t"+value); } //將結果儲存到檔案中 mapToFile(tMap, new File("result.txt")); } return; } } }; t.start(); } //將結果按照 "單詞:次數" 格式存在檔案中 private static void mapToFile(Map<String, Integer> src, File dst) { try { //對將要寫入的檔案建立通道 FileChannel fcout = new FileOutputStream(dst).getChannel(); //使用entrySet對結果集進行遍歷 for(Map.Entry<String,Integer> entry : src.entrySet()) { String key = entry.getKey(); int value = entry.getValue(); //將結果按照指定格式放到緩衝區中 ByteBuffer bBuf = ByteBuffer.wrap((key+":\t"+value).getBytes()); fcout.write(bBuf); bBuf.clear(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } class CountWords implements Runnable { private FileChannel fc; private FileLock fl; private MappedByteBuffer mbBuf; private HashMap<String, Integer> hm; public CountWords(File src, long start, long end) { try { //得到當前檔案的通道 fc = new RandomAccessFile(src, "rw").getChannel(); //鎖定當前檔案的部分 fl = fc.lock(start, end, false); //對當前檔案片段建立記憶體對映,如果檔案過大需要切割成多個片段 mbBuf = fc.map(FileChannel.MapMode.READ_ONLY, start, end); //建立HashMap例項存放處理結果 hm = new HashMap<String,Integer>(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { String str = Charset.forName("UTF-8").decode(mbBuf).toString(); //使用StringTokenizer分析單詞 StringTokenizer token = new StringTokenizer(str); String word; while(token.hasMoreTokens()) { //將處理結果放到一個HashMap中,考慮到儲存速度 word = token.nextToken(); if(null != hm.get(word)) { hm.put(word, hm.get(word)+1); } else { hm.put(word, 1); } } try { //釋放檔案鎖 fl.release(); } catch (IOException e) { e.printStackTrace(); } return; } //獲取當前執行緒的執行結果 public HashMap<String, Integer> getResult() { return hm; } }
以上程式碼是我自己實現的,主要思想是:
1.使用具有鍵值對結構的HashMap來快速存取;
2.由於檔案過大,用一個執行緒處理可能結果較慢,使用到併發機制;
3.IO操作比較耗時,所以使用了nio的相關內容;
4.最終結果要有序的話,可以使用TreeMap。
望同行給予批評指導,相信有更好的解決辦法和思路,如果能幫著優化以上程式碼,請給予留言,或者發郵件至[email protected],真誠歡迎各位程式設計愛好者與我討論相關技術問題。