The program solves the following problem: there are several files with text, a stream is created for each file through the thread pool to open and read the file and compile a List<String> from the read text, then it is assumed to use the Map<String, Long> common for all streams to collect statistics as follows - the key (key) will be a string from the list, respectively, and the value (value) is 1, but if such a key is already present, i.e. in the list and in other lists there are identical strings, then increment the value by one

If the first file contains the lines "Hello", the second, respectively, two lines "Java" and "World", and the third file is "Hello", then the resulting map should look like this:

 {"Hello"=2,"Java"=1, "World"=1} 

My implementation: Everything works correctly

In the makeStatistics () method, an instance of the class is created (Statistic), where the general map is located, then the makeStatistics () method uses the getFilesPaths () method to compute paths to files with the .txt extension and put them into a list, then creates a fixed pool of threads of the number of text files in the folder (1 file - 1 stream), then a loop creates instances of classes for the stream ReaderThread, an instance of the class (Statistic) with the general map and the path to the file is passed to its constructor

 public ArrayList<Path> getFilesPaths() { ArrayList<Path> filesNames = null; try (Stream<Path> files = Files.walk(Paths.get("D:\\DIR"))) { filesNames= files .filter(file -> file.toString().endsWith(".txt")) .collect(Collectors.toCollection(ArrayList::new)); filesNames.forEach(System.out::println); } catch (IOException e) { e.printStackTrace(); } return filesNames; } public void makeStatistics() { Statistic statistic = new Statistic(); ArrayList<Path> logPaths = getFilesPaths(); ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(logPaths.size()); for (Path filePath : logPaths) { ReaderThread logReader = new ReaderThread(statistic, filePath); executor.execute(logReader); } executor.shutdown(); } 

The stream class looks like this; it simply reads the file, along the path passed to it and saves the lines to the list, then in the line statistic.setStatisticMap (textLines); the list is sent to change the general map in the class Statistic

 public class ReaderThread implements Runnable { private Path filePath; private Statistic statistic; public ReaderThread(Statistic statistic, Path filePath) { this.filePath = filePath; this.statistic = statistic; } public Path getFile() { return filePath; } public void run() { List<String> textLines; try(Stream<String> lineStream = Files.newBufferedReader(filePath).lines()) { textLines = lineStream .collect(Collectors.toCollection(ArrayList::new)); statistic.setStatisticMap(textLines); } catch (IOException e) { e.printStackTrace(); } } 

And here is a class with a common map:

 public class Statistic { private Map<String, Long> statisticMap; public Statistic() { statisticMap = new HashMap<>(); } public synchronized void setStatisticMap(List<String> logLines) { statisticMap = logLines.stream() .collect(Collectors.toMap(Function.identity(), v -> 1L, Long::sum)); System.out.println(statisticMap); } public Map<String, Long> getStatisticMap() { return statisticMap; } 

The program displays the correct result, but I am interested in how and in what place to get access to the already constructed general map (statisticMap), i.e. how and where to get access to statisticMap after it is formed flows and it will be filled?

  • Well, you have a getStatisticMap method that returns a map. I do not understand what the problem is - Senior Pomidor
  • @Senior Pomidor, if in the method makeStatistics() after executor.shutdown(); write System.out.println(statistic.getMinuteStatisticMap()); {} empty map will be displayed and it will be displayed before the threads start working - Capt. Somov pm

1 answer 1

 executor.shutdown(); 

Will not wait for all transferred tasks. The easiest way is to use the invokeAll method and slightly modify the implementation.

 public class ReaderThread implements Callable<Object> { private Path filePath; private Statistic statistic; public ReaderThread(Statistic statistic, Path filePath) { this.filePath = filePath; this.statistic = statistic; } public Path getFile() { return filePath; } @Override public Object call() throws Exception { List<String> textLines; try (Stream<String> lineStream = Files.newBufferedReader(filePath).lines()) { textLines = lineStream.collect(Collectors.toCollection(ArrayList::new)); statistic.setStatisticMap(textLines); } catch (IOException e) { e.printStackTrace(); } return null; } } 

And call it through executor like this:

  List<ReaderThread> tasks = new ArrayList<>(); for (Path filePath : logPaths) { tasks.add(new ReaderThread(statistic, filePath)); } try { executor.invokeAll(tasks); } catch (InterruptedException e) { e.printStackTrace(); } finally { executor.shutdown(); } System.out.println(statistic.getStatisticMap());