My Portfolio and Work

Java Streams

Java Streams was introduced in Java 8. In this article we will see how we can use Java Streams to write a function in a more elegant and fancy way.

If you like to learn how to write few lines that can solve complex problems then you should give Java Streams a try.

The Problem

Let’s start with a problem statement, think about how we can solve it using loops and if-else statements, then we will see how to solve it using Java Streams.

You have been asked to write a function to return a list of the top N products from a list of products that appear in customer comments or reviews.

Solving Problems The Old Way

Before Java Streams, this problem can solved as follows:

List<String> topProductsClassic(List<String> reviews,
                            int N,
                            List<String> products)
{
    Map<String, Integer> productsCount = new HashMap<>();
    for (String review: reviews){
        String[] words = review.split("\\s+");
        for (String word: words){
            if (products.contains(word)){
                if (productsCount.containsKey(word)){
                    int count = productsCount.get(word) + 1;
                    productsCount.put(word, count);
                } else {
                    productsCount.put(word, 1);
                }
            }
        }
    }
    List<Map.Entry<String,Integer>> sortedEntries = new ArrayList<>(productsCount.entrySet());
    sortedEntries.sort(Map.Entry.<String, Integer>comparingByValue().reversed());
    List<String> results = new ArrayList<>();
    sortedEntries.subList(0, N).forEach(e -> {
        results.add(e.getKey());
    });
    return results;
}

You can notice something here. There is a data flow from one step to another, like a pipe-and-filter stream. This is your indicator that this problem can be solved using functional programming, or Java Streams.

Java Streams

Now let’s solve the same problem using Java Streams.

List<String> topProducts(List<String> reviews,
                        int N,
                        Set<String> products)
{
    return reviews.parallelStream()
        .flatMap(review -> Arrays.stream(review.trim().split("\\s+")))
        .map(String::toLowerCase)
        .filter(products::contains)
        .collect(Collectors.groupingByConcurrent(Function.identity(), Collectors.counting()))
        .entrySet()
        .stream()
        .sorted(((o1, o2) -> o2.getValue().compareTo(o1.getValue())))
        .map(Map.Entry::getKey)
        .limit(N)
        .collect(Collectors.toCollection(ArrayList::new));
}

Java Streams supports multi-threding out of the box. In the above example, we used parallelStream to stream reviews which will run the next steps in multiple threads under the hood.

Let’s hava a look at each function we used above to see how they work (Reference: Java 8 Docs):

Conclusion

Java Streams was introduced in Java 8. It has intermidate and terminal operations to process streams. It increases the readability of code by grouping the operations applied to the same data in one stream set of operations. It supports both serial and parrallel steaming.

Happy coding!