java – Counting words and word occurrences in a .txt file with optional filters

As I’m doing the MOOC Java course I decided to write a small program to count the word occurrences that accepts as the first (mandatory) input a path to a text file (extension not enforced) and as many words you want to check (optional). It prints out the results to the terminal. Checked with books in .txt format from Project Gutenberg.

This is my first ever Java program, thank you.

package com.my.name;

public class Main {

    public static void main(String() args) {
        if (args.length == 0) {
            throw new IllegalArgumentException("Required file name");
        }

        Option option = new Option();


        if (args.length > 1) {
            for (int i = 1; i < args.length; i++) {
                option.addWords(args(i));
            }
        }

        Stats stats = new Stats(option);
        String fileName = args(0);


        TextFileReader textReader = new TextFileReader(fileName, stats);
        textReader.readTest();

        stats.printOccurrences();
    }
}
package com.my.name;

import java.util.ArrayList;
import java.util.Locale;

public class Option {
    private final ArrayList<String> words;

    public Option() {
        this.words = new ArrayList<>();
    }

    public void addWords(String word) {
        this.words.add(word.toLowerCase(Locale.ROOT));
    }

    public boolean hasFilters() {
        return !(this.words.size() == 0);
    }

    public boolean hasWord(String word) {
        return this.words.contains(word);
    }
}
package com.my.name;

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Locale;
import java.util.regex.Pattern;

public class TextFileReader {

    private final String fileName;
    private final Stats stats;

    public TextFileReader(String fileName, Stats stats) {
        this.fileName = fileName;
        this.stats = stats;
    }

    public void readTest() {
        try (BufferedReader reader = Files.newBufferedReader(Paths.get(this.fileName))) {
            String line;
            Pattern pattern = Pattern.compile("(^a-zA-Z)"); // @TODO if needed
            while ((line = reader.readLine()) != null) {
                String() words = line.split(" ");
                for (String word : words) {
                    word = pattern.matcher(word).replaceAll("").toLowerCase(Locale.ROOT);
                    if (word.isBlank()) continue;
                    this.stats.mapWordToCount(word);
                }
            }
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }
}

package com.my.name;

import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;

public class Stats {
    private final Map<String, Integer> wordCounts;
    private final Option option;
    private int numberOfWords;

    public Stats(Option option) {
        this.wordCounts = new HashMap<>();
        this.option = option;
        this.numberOfWords = 0;
    }

    public void mapWordToCount(String word) {
        this.numberOfWords++;
        this.wordCounts.merge(word, 1, Integer::sum);
    }

    public void printOccurrences() {
        boolean hasFilters = option.hasFilters();
        Map<String, Integer> topTen = this.wordCounts
                .entrySet()
                .stream()
                .filter(w -> !hasFilters || option.hasWord(w.getKey()))
                .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
                .limit(20L)
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));

        for (String word : topTen.keySet()) {
            System.out.println("Word " + " | " + "u001B(31m" + word + "u001B(0m" + " | " + " occurred " + wordCounts.get(word) + " times");
        }

        System.out.println("Total words: " + this.numberOfWords);
    }
}

If this is the right place I would also like to ask if you could explain me how on earth the .sorted() and .collect() work in my code, couldn’t figure out how to order a Map so that was something found on StackOverflow.

Thanks again