Search

Dark theme | Light theme
Showing posts with label Java 12. Show all posts
Showing posts with label Java 12. Show all posts

September 10, 2019

Java Joy: Transform Elements In Stream Using a Collector

Using the Stream API and the map method we can transform elements in a stream to another object. Instead of using the map method we can also write a custom Collector and transform the elements when we use the collect method as terminal operation of the stream.

First we have an example where we transform String value using the map method:

package mrhaki;

import java.util.List;
import java.util.stream.Collectors;

public class CollectorString {
    public static void main(String[] args) {
        final var items = List.of("JFall", "JavaZone", "CodeOne");

        final List<String> upper =
                items.stream()
                     .map(String::toUpperCase)
                     .collect(Collectors.toUnmodifiableList());
        
        assert upper.equals(List.of("JFALL", "JAVAZONE", "CODEONE"));
    }
}

In our next example we don't use the map method, but we write a custom Collector using the Collector.of method. As first argument we must provide the data structure we want to add elements too, the so-called supplier, which is an ArrayList. The second argument is an accumulator where we add each element from the stream to the list and transform the value. The third argument is the combiner and here we combine multiple List instances to one List instance. The last argument is a finisher and we make an immutable List to be returned.

package mrhaki;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collector;

public class CollectorString1 {
    public static void main(String[] args) {
        final var items = List.of("JFall", "JavaZone", "CodeOne");

        final List<String> upper =
                items.stream()
                     // Use collector to transform values
                     // in the items List.
                     .collect(upperCollect());

        assert upper.equals(List.of("JFALL", "JAVAZONE", "CODEONE"));
    }

    private static Collector<String, ?, List<String>> upperCollect() {
        return Collector.of(
                // First we specify that we want to add
                // each element from the stream to an ArrayList.
                () -> new ArrayList<String>(),

                // Next we add each String value to the list
                // and turn it into an uppercase value.
                (list, value) -> list.add(value.toUpperCase()),

                // Next we get two lists we need to combine,
                // so we add the values of the second list
                // to the first list.
                (first, second) -> { first.addAll(second); return first; },

                // Finally (and optionally) we turn the 
                // ArrayList into an unmodfiable List.
                list -> Collections.unmodifiableList(list));
    }
}

Written with Java 12.

September 9, 2019

Java Joy: Combining Predicates

In Java we can use a Predicate to test if something is true or false. This is especially useful when we use the filter method of the Java Stream API. We can use lambda expressions to define our Predicate or implement the Predicate interface. If we want to combine different Predicate objects we can use the or, and and negate methods of the Predicate interfaces. These are default methods of the interface and will return a new Predicate.

Let's start with an example where we have a list of String values. We want to filter all values that start with Gr or with M. In our first implementation we use a lambda expression as Predicate and implements both tests in this expression:

package mrhaki;

import java.util.List;
import java.util.stream.Collectors;

public class PredicateComposition1 {
    public static void main(String[] args) {
        final var items = List.of("Groovy", "Gradle", "Grails", "Micronaut", "Java", "Kotlin");

        final List<String> gr8Stuff =
                items.stream()
                     // Use lambda expression with both tests as Predicate.
                     .filter(s -> s.startsWith("Gr") || s.startsWith("M"))
                     .collect(Collectors.toUnmodifiableList());

        assert gr8Stuff.size() == 4 : "gr8Stuff contains 4 items";
        assert gr8Stuff.contains("Groovy");
        assert gr8Stuff.contains("Gradle");
        assert gr8Stuff.contains("Grails");
        assert gr8Stuff.contains("Micronaut");
    }
}

We will rewrite the previous example and introduce the startsWith method that returns a new Predicate. Then in our filter method we use the or method of the Predicate object to combine the two Predicate objects:

package mrhaki;

import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class PredicateComposition2 {
    public static void main(String[] args) {
        final var items = List.of("Groovy", "Gradle", "Grails", "Micronaut", "Java", "Kotlin");

        final List<String> gr8Stuff =
                items.stream()
                     // Use the Predicate.or method to combine two Predicate objects.
                     .filter(startsWith("Gr").or(startsWith("M")))
                     .collect(Collectors.toUnmodifiableList());

        assert gr8Stuff.size() == 4 : "gr8Stuff contains 4 items";
        assert gr8Stuff.contains("Groovy");
        assert gr8Stuff.contains("Gradle");
        assert gr8Stuff.contains("Grails");
        assert gr8Stuff.contains("Micronaut");
    }

    // Create a predicate to check if String value starts with a given value.
    private static Predicate<String> startsWith(final String begin) {
        return s -> s.startsWith(begin);
    }
}

In the following example we use the negate and and method to find all values that do not start with Gr and with a length less than 8 characters:

package mrhaki;

import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class PredicateComposition3 {
    public static void main(String[] args) {
        final var items = List.of("Groovy", "Gradle", "Grails", "Micronaut", "Java", "Kotlin");

        final List<String> otherStuff =
                items.stream()
                     // Find all values that do not start with "Gr"
                     // and have less than 8 characters.
                     .filter(startsWith("Gr").negate().and(smallerThan(8)))
                     .collect(Collectors.toUnmodifiableList());

        assert otherStuff.size() == 2 : "otherStuff contains 2 items";
        assert otherStuff.contains("Java");
        assert otherStuff.contains("Kotlin");
    }

    // Create a predicate to check if String value starts with a given value.
    private static Predicate<String> startsWith(final String begin) {
        return s -> s.startsWith(begin);
    }

    // Create a predicate to check if String value has 
    // less characters than the given size.
    private static Predicate<String> smallerThan(final int size) {
        return s -> size >= s.length();
    }
}

In our previous example we can replace the negate method call on our predicate with the static Predicate.not method. The predicate is than an argument and is just another way to express the same predicate:

package mrhaki;

import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class PredicateComposition4 {
    public static void main(String[] args) {
        final var items = List.of("Groovy", "Gradle", "Grails", "Micronaut", "Java", "Kotlin");

        final List<String> otherStuff =
                items.stream()
                     // Find all values that do not start with "Gr",
                     // using Predicate.not instead of negate, 
                     // and have less than 8 characters.
                     .filter(Predicate.not(startsWith("Gr")).and(smallerThan(8)))
                     .collect(Collectors.toUnmodifiableList());

        assert otherStuff.size() == 2 : "otherStuff contains 2 items";
        assert otherStuff.contains("Java");
        assert otherStuff.contains("Kotlin");
    }

    // Create a predicate to check if String value starts with a given value.
    private static Predicate<String> startsWith(final String begin) {
        return s -> s.startsWith(begin);
    }

    // Create a predicate to check if String value has 
    // less characters than the given size.
    private static Predicate<String> smallerThan(final int size) {
        return s -> size >= s.length();
    }
}

Written with Java 12.