2017-10-13 331 views
0

我正在嘗試通過「Cay S Horstman」來解決書籍「真正不耐煩的Java SE 8」的第6章練習5。這裏是問題:如何在ConcurrentHashMap中正確使用「merge」方法?

編寫一個應用程序,其中多個線程讀取文件集合中的所有單詞。使用ConcurrentHashMap>來跟蹤每個單詞出現的文件。使用合併方法更新地圖。

我試圖解決這個練習是這樣的:我 創建4個文件FILE1.TXT,FILE2.TXT,file3.txt,file4.txt

FILE1.TXT有以下內容:

Word1 

FILE2.TXT具有以下內容:

Word1 Word2 

file3.txt具有以下內容:

Word1 Word2 Word3 

file4.txt有以下內容:

Word1 Word2 Word3 Word4 

我實現了一個類「Problem5.java」那得到的上述4個文件在指定的目錄列表。這個類有一個靜態的ConcurrentHashMap「stringToFileMap」,它將使用「merge」方法進行更新。此類爲每個文件創建一個StringToFileMapper對象,然後使用executor服務調用所有可調用對象。一旦從執行器服務返回期貨,它就打印出併發哈希映射的內容。

Problem5.java

package problem5; 

import java.io.File; 
import java.util.ArrayList; 
import java.util.Collection; 
import java.util.List; 
import java.util.Set; 
import java.util.concurrent.*; 

public class Problem5 { 
public static final ConcurrentHashMap<String, Set<File>> stringToFileMap = new ConcurrentHashMap<>(); 
private File[] files; 
private ExecutorService executorService; 
public Problem5(String dirName){ 
    executorService = Executors.newFixedThreadPool(2); 
    File dir = new File(dirName); 
    files = dir.listFiles((dir1, name) -> name.endsWith(".txt")); 
} 

public void execute() throws InterruptedException, ExecutionException { 
    List<Future<Long>> futureList; 
    Collection<StringToFileMapper> callables = new ArrayList<>(); 
    for(File file: files){ 
     callables.add(new StringToFileMapper(file)); 
    } 
    futureList = executorService.invokeAll(callables); 
    stringToFileMap.forEach((String key, Set<File> files) -> { 
     StringBuilder fileNames = new StringBuilder(); 
     for(File file: files){ 
      fileNames.append(file.getName()); 
      fileNames.append(", "); 

     } 
     System.out.println(key+" is present in files "+fileNames.toString()); 
    }); 
    System.out.println("Hashmap size = "+ stringToFileMap.size()); 
    executorService.shutdown(); 
    stringToFileMap.clear(); 
} 
} 

我實現了一個可調用的類 「StringToFileMapper.java」 從文件和更新的ConcurrentHashMap 「stringToFileMap」 讀字。

StringToFileMapper.java:

package problem5; 

import java.io.BufferedReader; 
import java.io.File; 
import java.io.FileReader; 
import java.math.BigInteger; 
import java.util.HashSet; 
import java.util.Set; 
import java.util.concurrent.Callable; 
import java.util.concurrent.ConcurrentHashMap; 
import java.util.function.BiFunction; 
import java.util.stream.Stream; 

public final class StringToFileMapper implements Callable<Long> { 

private final File file; 
public StringToFileMapper(File file){ 
    this.file = file; 
} 


@Override 
public Long call() throws Exception { 
    Long count = 0l; 
    try (BufferedReader reader = new BufferedReader(new FileReader(this.file))){ 
     Set<File> fileSet = ConcurrentHashMap.newKeySet(); 
     fileSet.add(this.file); 
     Stream<String> lineStream = reader.lines(); 
     lineStream.forEach(line -> { 
      String[] words = line.split(" "); 
      for(String word: words){ 
       //count = count.add(BigInteger.ONE); 
       BiFunction<Set<File>, Set<File>, Set<File>> reMappingFunction = (Set<File> oldSet, Set<File> newSet) -> { 
        oldSet.addAll(newSet); 
        return oldSet; 
       }; 
       System.out.println("Word " +word+" is in "+ this.file.getName()); 

       Problem5.stringToFileMap.merge(word, fileSet, reMappingFunction); 
      } 

     }); 
    } 
    return count; 
} 
} 

正如你可以在上面看到,我用雙功能 「reMappingFunction」 隨着ConcurrentHashMap的 「合併」 功能。

Problem5.java將被主函數調用,該函數用文本文件的目錄路徑實例化它並調用其「execute」方法。

import problem1.Problem1; 
import problem3.Problem3; 
import problem5.Problem5; 

import java.util.concurrent.ExecutionException; 

public class Main { 
public static void main(String[] args) throws InterruptedException, ExecutionException { 
    Problem5 problem5 = new Problem5("src/problem1"); 
    problem5.execute(); 
} 
} 

當我執行上面的程序時,它有時打印正確的輸出,並在其他時間打印錯誤的輸出。

正確的輸出

objc[36588]: Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/bin/java (0x1064e64c0) and /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/libinstrument.dylib (0x1065ae4e0). One of the two will be used. Which one is undefined. 
Word Word1 is in file1.txt 
Word Word1 is in file2.txt 
Word Word2 is in file2.txt 
Word Word1 is in file3.txt 
Word Word2 is in file3.txt 
Word Word3 is in file3.txt 
Word Word1 is in file4.txt 
Word Word2 is in file4.txt 
Word Word3 is in file4.txt 
Word Word4 is in file4.txt 
Word4 is present in files file4.txt, 
Word2 is present in files file4.txt, file2.txt, file3.txt, 
Word3 is present in files file4.txt, file3.txt, 
Word1 is present in files file4.txt, file1.txt, file2.txt, file3.txt, 
Hashmap size = 4 

Process finished with exit code 0 

錯誤輸出

objc[36672]: Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/bin/java (0x10c8f04c0) and /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/libinstrument.dylib (0x10c9b84e0). One of the two will be used. Which one is undefined. 
Word Word1 is in file2.txt 
Word Word1 is in file1.txt 
Word Word2 is in file2.txt 
Word Word1 is in file3.txt 
Word Word2 is in file3.txt 
Word Word3 is in file3.txt 
Word Word1 is in file4.txt 
Word Word2 is in file4.txt 
Word Word3 is in file4.txt 
Word Word4 is in file4.txt 
Word4 is present in files file4.txt, 
Word2 is present in files file4.txt, file2.txt, file1.txt, file3.txt, 
Word3 is present in files file4.txt, file3.txt, 
Word1 is present in files file4.txt, file2.txt, file1.txt, file3.txt, 
Hashmap size = 4 

Process finished with exit code 0 

正如你可以看到在上面的輸出錯誤,從問題5.java併發散列映射輸出是錯誤的,在那裏作爲輸出從StringToFileMapper.java中的sysout是正確的。

我無法弄清楚爲什麼上面的程序有時不工作。

+0

「編寫一個應用程序,其中多個線程」我根本沒有看到你在哪裏使用多線程。你沒有實現runnable也沒有擴展線程類 –

+0

我的不好。我剛剛意識到這是一個Java 8的新功能,我的學校只教我們Java 7 –

+0

@JeremiahStillings我使用executorService = Executors.newFixedThreadPool(2)創建多個線程;並使用executorService.invokeAll(callables)調用多個線程; – krishna2642

回答

0

我想出瞭解決這個問題的辦法。 BiFunction參數合併方法必須返回一個新的集合,而不是修改現有的集合。正確的「BiFunction」參數是:

BiFunction<Set<File>, Set<File>, Set<File>> reMappingFunction = (Set<File> oldSet, Set<File> newSet) -> { 
        Set<File> temp = new HashSet<>(); 
        temp.addAll(newSet); 
        temp.addAll(oldSet); 
        return temp; 
       };