Improving the Java 8 way of finding the most common words in "War and Peace"

Viewed 1925

I read this problem in Richard Bird's book: Find the top five most common words in War and Peace (or any other text for that matter).

Here's my current attempt:

public class WarAndPeace {
    public static void main(String[] args) throws Exception {
        Map<String, Integer> wc =
            Files.lines(Paths.get("/tmp", "/war-and-peace.txt"))
            .map(line -> line.replaceAll("\\p{Punct}", ""))
            .flatMap(line -> Arrays.stream(line.split("\\s+")))
            .filter(word -> word.matches("\\w+"))
            .map(s -> s.toLowerCase())
            .filter(s -> s.length() >= 2)
            .collect(Collectors.toConcurrentMap(
                    w -> w, w -> 1, Integer::sum));

        wc.entrySet()
            .stream()
            .sorted((e1, e2) -> Integer.compare(e2.getValue(), e1.getValue()))
            .limit(5)
            .forEach(e -> System.out.println(e.getKey() + ": " + e.getValue()));

    }
}

This definitely looks interesting and runs reasonably fast. On my laptop it prints the following:

$> time java -server -Xmx10g -cp target/classes tmp.WarAndPeace
the: 34566
and: 22152
to: 16716
of: 14987
a: 10521
java -server -Xmx10g -cp target/classes tmp.WarAndPeace  1.86s user 0.13s system 274% cpu 0.724 total

It usually runs in under 2 seconds. Can you suggest further improvements to this from an expressiveness and a performance standpoint?

PS: If you are interested in the rich history of this problem, see here.

2 Answers
Related