Archive for category lambdas

Writing your own custom Java stream collector

One of the common operations of the Collectors API introduced in Java 8 is the possibility to collect results into a result container like List, Set or Map. The following example uses the collect() method to generate a HashSet containing unique numbers:

Now suppose a given string and the need to compute some summaries on it, like the number of uppercase, lowercase, invalid chars and how many digits are present on that string.

Applying a reduce operation on each needed summary operation would result in more than one pass through the data, like this (here I am using the var keyword introduced in Java 10):

In the above code excerpt we are iterating three times through the string to compute the summaries. If we want to iterate just one time on the give string and get the desired results, we could fallback to the traditional imperative approach:

Despite the solution above, there is another one: writing your own custom stream collector. This custom collector can compute the number of uppercase, lowercase, invalid chars and how many digits are present on the given string, in a single pass through the data. It is possible to make it run in parallel with the Streams API as well.

The custom stream collector shown here uses the chars() method of the String class which returns an IntStream. The IntStream class contains a collect() method that computes a mutable reduction on the elements and returns its result in a container class.

The next example shows the container class code. It receives and accumulates each char of the String in the accept() method, thus categorizing it as a digit, uppercase char, lowercase char or as an invalid char.

The complete implementation of the collect() method which produces the result of the reduction in the CharSummaryStatistics class is shown below. As a new char arrives, it is categorized in the CharSummaryStatistics.accept() method. The CharSummaryStatistics.combine() method is used to merge partial results.

Have you ever written a custom Java stream collector? Please drop your comments here.

The complete source code can be found on GitHub.

As a reference for writing this post, this article is part of a series about Streams and it is a good reference for learning how to aggregate and collect with Streams. All the articles from this series about Streams can be found here.

Tags: , , , , , , , ,

Jcombiner: Combinations of collections for Java

JCombiner is a framework to generate combinations of collections for Java. I have written it in Java 11 using Java 9 modules (JPMS) and Gradle as build tool. JUnit 5 and Mockito are used for unit testing and Jacoco for code coverage. Streams and the Collectors API are extensively used throughout the development of JCombiner project.

Jcombiner’s source code is available under GitHub.

Code examples of its usage can be found on GitHub here. More examples can be found on this module inside JCombiner.

Share your comments about this framework here! Please feel free to contribute to it, more features are welcome!

Tags: , , , , , , , , , , , , , , , ,

Refactoring large conditional method using method references

Some years ago I wrote junit-parameters, which is basically a custom JUnit test runner that make it possible to add parameters to JUnit 4 test methods.

Browsing its source code SonarLint pointed me a large conditional if/else method from the ParameterTypeConverterFactory class:

This method converts the method parameter to its specific type based on its Class object. As it is few lines long, it showed me a good opportunity to refactor this code a little bit with a more elegant solution. This project has unit tests, so I could start refactoring it in small steps and start over again whether I have made a mistake.

I started by defining a functional interface called ParameterConverter:

and I created an immutable map which maps each Class object to its associated ParameterConverter instance (making use of method references):

Then I refactored the original conditional method to get the ParameterConverter instance from the convertersByClass map and mapping to an Optional instance in case it didn’t exist.

After those refactorings, SonarLint stopped warning me. Below is the modified version of the original method with some helper methods:

The complete code can be found at my GitHub here.

This was the first change of breaking this complicated conditional method into a more readable one. It was safe to do so because after each change I could run my unit tests suite to assert I haven’t broken anything. The next refactoring could be towards a more object-oriented approach like Replace Conditional with Polymorphism.

What did you think about this refactoring? Have you ever had such a situation? Drop your comments here!

Tags: , , , , , , , , , , ,

Streams in JDK 8: The Good, the Bad, and the Ugly

Great session in JavaOne 2017 about Streams and lambdas introduced in JDK8.

The session shows many examples of Java code using forEach() with side effects and how to refactor them to a functional approach using streams and the Collectors API.

What are your experiences using Streams and lambdas in JDK 8? Are you correctly using the Collectors API?

Tags: , , , , , , , ,

Converting a Map to a List in Java 8, Groovy and Ruby

Some days ago I was developing a task on a Gradle project and I faced with a situation where I had to convert a Map < String, List < String >> to List < Pair >, each pair containing the key and one element from the List<String>.

I decided to compare the solution in three different languages: Java 8 (using lambdas and the Streams API), Groovy and Ruby to see how concise and expressive they would be. Then, I created the Groovy code and it looked like this:

#!/usr/bin/env groovy

def map = ["a" : ["1", "2", "3"], "b" : ["4", "5", "6"], "c" : ["7"]]

println map.collectMany { key, values -> values.collect {value -> [key, value]}}

Running the above code, the result is below:

[[a, 1], [a, 2], [a, 3], [b, 4], [b, 5], [b, 6], [c, 7]]

The Ruby version looked like this:

#!/usr/bin/env ruby

map = { "a" => ["1", "2", "3"], "b" => ["4", "5", "6"], "c" => ["7"] }

p map.collect {|key, values| values.map {|value| [key, value] } }.flatten(1)

The Ruby program generated the following output:

[["a", "1"], ["a", "2"], ["a", "3"], ["b", "4"], ["b", "5"], ["b", "6"], ["c", "7"]]

Below is the Java 8 version, using lambdas, Streams and the Collectors API:

        
        Map> values = new HashMap<>();
        values.put("a", Arrays.asList("1", "2", "3"));
        values.put("b", Arrays.asList("4", "5", "6"));
        values.put("c", Collections.singletonList("7"));

        final List result = values
                .entrySet()
                .stream()
                .collect(ArrayList::new,
                        (pairs, entry) -> pairs.addAll(entry
                                .getValue()
                                .stream()
                                .map(value -> new Pair(entry.getKey(), value))
                                .collect(Collectors.toList())),
                        List::addAll);

private static class Pair {

    private final String first;
    private final String second;

    public Pair(String first, String second) {
        this.first = first;
        this.second = second;
    }

    @Override
    public String toString() {
        return "Pair{first='" + first + "', second='" + second + "'}";
    }
}

Running the Java 8 version produced the following output:

[Pair{first='a', second='1'}, Pair{first='a', second='2'}, 
Pair{first='a', second='3'}, Pair{first='b', second='4'}, 
Pair{first='b', second='5'}, Pair{first='b', second='6'}, 
Pair{first='c', second='7'}]

The Groovy and Ruby version are very expressive and concise. Note the use of the collectMany method on the Groovy version and the use of the flatten method on the Ruby version to flatten the result list into a single list of pairs.
The Java 8 version made use of the collect method of the Stream API, to collect the results in a list of Pair instances, each one holding the key and value of each element from the List< String >.

What do you think about this comparison? Leave your comments here!

Tags: , , , , , , ,

JavaCodeKata – Lambdas

I’ve came across that kata from @brjavaman and @yanaga to teach lambdas, one of the new features of JDK 8.

There are some unit tests to validate the solution. I’ve found it a good opportunity to exercise the use of lambdas so I decided to solve it. Below is my solution to this kata.

The first method should take the String list and sort all the String elements in ascending (ASCII) order:

    /**
     * This method should take the String List and sort all the String elements in ascending (ASCII) order.
     *
     * @return The sorted values in ascending ASCII order.
     */
    public List getSortedStrings() {
        return values
                .stream()
                .sorted()
                .collect(Collectors.toList());
    }

The other method should take the String list and:

  1. filter the elements that contains one or more digits
  2. transform (map) the remaining Strings into Integers
  3. sort the Integers in ascending order
        
    /**
     * This method should take the String List and:
     * 
    *
  1. filter the elements that contains one or more digits;
  2. *
  3. transform (map) the remaining Strings into Integers;
  4. *
  5. sort the Integers in ascending order.
  6. *
* * @return */ public List getSortedIntegers() { return values .stream() .filter(s -> s.matches("\\d+")) .map(Integer::valueOf) .sorted() .collect(Collectors.toList()); }

The last method should take the String list and:

  1. filter the elements that contains one or more digits
  2. transform (map) the remaining Strings into Integers
  3. sort the Integers in descending order
    
    /**
     * This method should take the String List and:
     * 
    *
  1. filter the elements that contains one or more digits;
  2. *
  3. transform (map) the remaining Strings into Integers;
  4. *
  5. sort the Integers in descending order.
  6. *
* * @return */ public List getSortedDescendingIntegers() { return values .stream() .filter(s -> s.matches("\\d+")) .map(Integer::valueOf) .sorted(Comparator.reverseOrder()) .collect(Collectors.toList()); }

Note that the steps filter the elements that contains one or more digits and transform (map) the remaining Strings into Integers are identical. So I decided to extract the partialĀ Stream into a method with the Extract Method refactoring support on IntelliJ IDEA:

    private Stream integersWithOneOrMoreDigits() {
        return values
                .stream()
                .filter(s -> s.matches("\\d+"))
                .map(Integer::valueOf);
    }

Then I refactored the the solution to use the new extracted method:

    
    public List getSortedIntegers() {
        return integersWithOneOrMoreDigits()
                .sorted()
                .collect(Collectors.toList());
    }
    public List getSortedDescendingIntegers() {
        return integersWithOneOrMoreDigits()
                .sorted(Comparator.reverseOrder())
                .collect(Collectors.toList());
    }

I re-run the tests and they all passed. What do you think about this solution? Do you suggest other ones?