2017-10-13 6 views
0

J'essaie de résoudre l'exercice 5, chapitre 6 du livre "Java SE 8 pour Really Impatient" par "Cay S Horstman". Voici la question:Comment utiliser correctement la méthode "merge" dans ConcurrentHashMap?

Ecrivez une application dans laquelle plusieurs threads lisent tous les mots d'une collection de fichiers. Utilisez un ConcurrentHashMap> pour suivre les fichiers dans lesquels chaque mot se produit. Utilisez la méthode de fusion pour mettre à jour la carte.

J'ai essayé de résoudre cet exercice comme celui-ci: j'ai créé 4 fichiers file1.txt, file2.txt, file3.txt, file4.txt

file1.txt a ces contenus:

Word1 

file2.txt a ces contenus:

Word1 Word2 

file3.txt a ces contenus:

Word1 Word2 Word3 

file4.txt a ces contenus:

Word1 Word2 Word3 Word4 

I mis en œuvre une classe "Problem5.java" qui obtient la liste des fichiers ci-dessus 4 dans le répertoire spécifié. Cette classe a un "ConcurrentHashMap statique" stringToFileMap "qui sera mis à jour en utilisant la méthode" merge ". Cette classe crée un objet StringToFileMapper pour chaque fichier, puis appelle tous les appelables en utilisant le service d'exécution. Une fois que les contrats à terme sont retournés par le service d'exécution, ils impriment le contenu de la carte de hachage simultanée.

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(); 
} 
} 

I mis en œuvre une classe appelable "StringToFileMapper.java" qui lit des mots à partir d'un fichier et mises à jour 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; 
} 
} 

Comme vous pouvez le voir ci-dessus, j'ai utilisé BiFunction "reMappingFunction" avec fonction "fusion" dans ConcurrentHashMap. Problem5.java sera appelé depuis la fonction principale qui l'instancie avec le chemin de répertoire des fichiers texte et appelle sa méthode "execute". Lorsque j'exécute le programme ci-dessus ci-dessus, il imprime parfois la sortie correcte et à d'autres moments il imprime la mauvaise sortie.

sortie correcte

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 

sortie Mauvais

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 

Comme vous pouvez le voir ci-dessus mauvaise sortie, sortie de hashmap simultanée dans 5.java problème est faux, alors que la sortie de sysout dans StringToFileMapper.java est correct.

Je ne suis pas capable de comprendre pourquoi le programme ci-dessus ne fonctionne pas parfois.

+0

"Écrire une application dans laquelle plusieurs threads" Je ne vois pas où vous utilisez plusieurs threads du tout. Vous n'implémentez pas la classe de thread exécutable ou extensible –

+0

Mon mauvais. Je viens de réaliser qu'il s'agissait d'une fonctionnalité Java 8 New, mon école ne nous enseigne que Java 7 –

+0

@JeremiahStillings Je crée plusieurs threads en utilisant executorService = Executors.newFixedThreadPool (2); et invoquant plusieurs threads en utilisant executorService.invokeAll (callables); – krishna2642

Répondre

0

J'ai trouvé la solution à ce problème. L'argument BiFunction de la méthode de fusion doit renvoyer un nouvel ensemble au lieu de modifier l'ensemble existant. L'argument "BiFunction" correct est:

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; 
       };