Wednesday, August 9, 2017

Java 8 - Collectors

Collectors

One of the main advantages of functional-style programming over an imperative approach:
you just have to formulate the result you want to obtain the “what” and not the steps you need to perform to obtain it—the “how.”

Collectors can be seen as advanced reductions.

Factory methods provided by the Collectors class offer three main functionalities:

  • Reducing and summarizing stream elements to a single value
  • Grouping elements
  • Partitioning elements

Examples

// count the number of dishes in the menu, using the collector returned by the counting factory method
long howManyDishes = menu.stream().collect(Collectors.counting());
// you can write this far more directly as
long howManyDishes = menu.stream().count();

// Calculate the average value of an Integer property of the items in the stream.
double avgCalories = menu.stream().collect(Collectors.averagingInt(Dish::getCalories));

// get the count, sum, average, maximum, and minimum of the calories contained in each dish with a single summarizing operation:
IntSummaryStatistics menuStatistics = menu.stream().collect(Collectors.summarizingInt(Dish::getCalories));

// joining internally makes use of a StringBuilder to append the generated strings into one.
String shortMenu = menu.stream().map(Dish::getName).collect(Collectors.joining(", "));

// maxBy - An Optional wrapping the maximal element in this stream according to the given comparator or Optional.empty() if the stream is empty.
Optional<Dish> mostCalorieDish  = menu.stream().collect(Collectors.maxBy(Comparator.comparingInt(Dish::getCalories)));

// reducing - Reduce the stream to a single value starting from an initial value used as accumulator and iteratively combining it with each item of the stream using a BinaryOperator.
// using Collectors.reducing to get maximum calorie value
Optional<Dish> mostCalorieDish2 = menu.stream().collect(Collectors.reducing((d1, d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2));
Integer mostCalorieValue = menu.stream().collect(Collectors.reducing(0, Dish::getCalories, Integer::max));

Stream.reduce method is meant to combine two values and produce a new one; it’s an immutable reduction.
Stream.collect method is designed to mutate a container to accumulate the result it’s supposed to produce.

groupingBy - Group the items in the stream based on the value of one of their properties and use those values as keys in the resulting Map.
Map<Dish.Type, List<Dish>> dishesByType = menu.stream().collect(Collectors.groupingBy(Dish::getType));
You pass to the groupingBy method a Function (expressed in the form of a method reference) extracting the corresponding Dish.Type for each Dish in the stream. We call this Function a classification function because it’s used to classify the elements of the stream into different groups.

Map<Dish.Type, Long> typesCount = menu.stream().collect(Collectors.groupingBy(Dish::getType, Collectors.counting()));
The result can be the following Map: {MEAT=2, FISH=4, OTHER=3}

collectingAndThen - Wrap another collector and apply a transformation function to its result.
int howManyDishes = menuStream.collect(collectingAndThen(toList(), List::size));

Map<Dish.Type, Optional<Dish>> mostCaloricByType = menu.stream().collect(
Collectors.groupingBy(Dish::getType,
Collectors.maxBy(Comparator.comparingInt(Dish::getCalories))));
There cannot be empty optional as value - key will not be present!
We can use Collectors.collectingAndThen factory method to get Dish instead Optional<Dish>
Map<Dish.Type, Dish> mostCaloricDishesByTypeWithoutOptionals = menu.stream().collect(
Collectors.groupingBy(Dish::getType,
        Collectors.collectingAndThen(
        Collectors.maxBy(Comparator.comparingInt(Dish::getCalories)),
                        Optional::get)));
// using Optional.get is safe because the reducing collector will never return an Optional.empty()

Partitioning

Partitioning is a special case of grouping: having a predicate (a function returning a boolean), called a partitioning function, as a classification function. The fact that the partitioning function returns a boolean means the resulting grouping Map will have a Boolean as a key type and therefore there can be at most two different groups—one for true and one for false.

Map<Boolean, List<Dish>> partitionByVegeterian = menu.stream().collect(Collectors.partitioningBy(Dish::isVegetarian));
same functionality you can get with:
List<Dish> vegetarianDishes = menu.stream().filter(Dish::isVegetarian).collect(Collectors.toCollection(ArrayList::new));

Summary


  • Collect is a terminal operation that takes as argument various recipes (called collectors) for accumulating the elements of a stream into a summary result.
  • Predefined collectors include reducing and summarizing stream elements into a single value, such as calculating the minimum, maximum, or average.
  • Predefined collectors let you group elements of a stream with groupingBy and partition elements of a stream with partitioningBy.
  • Collectors compose effectively to create multilevel groupings, partitions, and reductions.
  • You can develop your own collectors by implementing the methods defined in the Collector interface.

Ref

Sunday, August 6, 2017

Java 8 - Streams

A stream is a sequence of elements from a source that supports data processing operations.

Streams make use of internal iteration: the iteration is abstracted away through operations such as filter, map, and sorted.

There are two types of stream operations: intermediate and terminal operations.
Intermediate operations such as filter and map return a stream and can be chained together. They’re used to set up a pipeline of operations but don’t produce any result.
Terminal operations such as forEach and count return a nonstream value and process a stream pipeline to return a result.

The elements of a stream are computed on demand.

You can filter and slice a stream using the filter, distinct, skip, and limit methods.
You can extract or transform elements of a stream using the map and flatMap methods.
You can find elements in a stream using the findFirst and findAny methods. You can match a given predicate in a stream using the allMatch, noneMatch, and anyMatch methods.
These methods make use of short-circuiting: a computation stops as soon as a result is found; there’s no need to process the whole stream.
You can combine all elements of a stream iteratively to produce a result using the reduce method, for example, to calculate the sum or find the maximum of a stream.
Some operations such as filter and map are stateless; they don’t store any state. Some operations such as reduce store state to calculate a value. Some operations such as sorted and distinct also store state because they need to buffer all the elements of a stream before returning a new stream. Such operations are called stateful operations.
There are three primitive specializations of streams: IntStream, DoubleStream, and LongStream. Their operations are also specialized accordingly.

Examples


      /** get dish names that have less than 400 calories sorted by calories */
    public static List<String> getLowCaloricDishesNames(List<Dish> dishes) {
        return dishes.stream()
                .filter(d -> d.getCalories() < 400)
                .sorted(Comparator.comparing(Dish::getCalories))
                .map(Dish::getName)
                .collect(Collectors.toList());
    }


    //* finds the first square that’s divisible by 3 */    
    List<Integer> numbers= Arrays.asList(1, 2, 3, 4, 5); 
    Optional<Integer> firstSquareDivisibleByThree = numbers.stream()
             .map(x -> x * x) 
             .filter(x -> x % 3 == 0) 
             .findFirst(); // 9
    
    /** sum the elements of a list of numbers */
    List<Integer> numbers = Arrays.asList(3,4,5,1,2);
    int sum = numbers.stream().reduce(0, Integer::sum);

The problem with this code is that there’s an insidious boxing cost. Behind the scenes each Integer needs to be unboxed to a primitive before performing the summation - better to call sum method on stream:
    int sum = numbers.stream().sum();

    // find maximum element using reduce function
    int max = numbers.stream().reduce(0, (a, b) -> Integer.max(a, b));

    // when there is no initial value, return value is Optional
    Optional<Integer> min = numbers.stream().reduce(Integer::min);
    min.ifPresent(System.out::println);

    // count using map-reduce pattern
    int count = numbers.stream().map(d -> 1).reduce(0, (a, b) -> a + b);

Common data processing idiom is finding whether some elements in a set of data match a given property. The Streams API provides such facilities through the allMatch, anyMatch, noneMatch, findFirst, and findAny methods of a stream.

The anyMatch method can be used to answer the question “Is there an element in the stream matching the given predicate?”

    List<Integer> numbers = Arrays.asList(3,4,5,1,2);
    boolean anyMatch = numbers.stream().anyMatch(n -> n == 5);



Creating streams

Streams can be created not only from a collection but also from values, arrays, files, and specific methods such as iterate and generate.

        // Stream.of
        Stream<String> stream = Stream.of("Java 8", "Lambdas", "In", "Action");
        stream.map(String::toUpperCase).forEach(System.out::println);

        // Stream.empty
        Stream<String> emptyStream = Stream.empty();

        // Arrays.stream
        int[] numbers = {2, 3, 5, 7, 11, 13};
        System.out.println(Arrays.stream(numbers).sum());

The Streams API provides two static methods to generate a stream from a function: Stream.iterate and Stream.generate. These two operations let you create what we call an infinite stream: a stream that doesn’t have a fixed size like when you create a stream from a fixed collection.
        // Stream.iterate
        Stream.iterate(0, n -> n + 2)
              .limit(10)
              .forEach(System.out::println);

        // stream of 1s with Stream.generate
        IntStream.generate(() -> 1)
                 .limit(5)
                 .forEach(System.out::println);

        // fibonnaci with iterate
        Stream.iterate(new int[]{0, 1}, t -> new int[]{t[1],t[0] + t[1]})
              .limit(10)
              . map(t -> t[0])
              .forEach(System.out::println);

       // find out the number of unique words in a file
        Files.lines(Paths.get("/data.txt"), Charset.defaultCharset())
                                 .flatMap(line -> Arrays.stream(line.split(" ")))
                                 .distinct()
                                 .count();
You use flatMap to produce one flattened stream of words instead of multiple streams of words for each line.

Ref

Java 8 in Action book
http://www.baeldung.com/java-8-streams
http://www.mkyong.com/java8/java-8-streams-filter-examples/

Java 8 - Lambdas

Behavior parameterization

Behavior parameterization is the ability for a method to take multiple different behaviors (or strategies) as parameters and use them internally to accomplish different behaviors.
Behavior parameterization lets you make your code more adaptive to changing requirements and saves on engineering efforts in the future.
Passing code is a way to give new behaviors as arguments to a method. But it’s verbose prior to Java 8.
Anonymous classes helped a bit before Java 8 to get rid of the verbosity associated with declaring multiple concrete classes for an interface that are needed only once.
The Java API contains many methods that can be parameterized with different behaviors, which include sorting, threads, and GUI handling.

Strategy design pattern lets you define a family of algorithms, encapsulate each algorithm (called a strategy), and select an algorithm at run-time.

Lambda expression


A lambda expression can be understood as a concise representation of an anonymous function that can be passed around: it doesn’t have a name, but it has a list of parameters, a body, a return type, and also possibly a list of exceptions that can be thrown.
Let’s break it down:
  • Anonymous— We say anonymous because it doesn’t have an explicit name like a method would normally have: less to write and think about!
  • Function— We say function because a lambda isn’t associated with a particular class like a method is. But like a method, a lambda has a list of parameters, a body, a return type, and a possible list of exceptions that can be thrown.
  • Passed around— A lambda expression can be passed as argument to a method or stored in a variable.
  • Concise— You don’t need to write a lot of boilerplate like you do for anonymous classes.

A lambda expression is composed of parameters, an arrow, and a body.

Runnable r = () -> System.out.println("Hello!");
Runnable r = () -> {};
Callable<String> c () -> "Samara";
Callable<String> c () -> "Samara" + 12;
(Integer i) -> {return "Samara" + 22;} // return is control-flow statement. it has to be in curly braces
(String s) -> {return "S";} // cannot stay just "S" when you have control-flow statement.

You can use a lambda expression in the context of a functional interface.


Functional interface

Functional interface is an interface that specifies exactly one abstract method.
@FunctionalInterface - optional annotation used to indicate that the interface is intended to be a functional interface.

Examples:
  • java.util.Comparator<T>
  • java.lang.Runnable
  • java.util.concurrent.Callable<V>
  • java.util.function.Predicate<T>

@FunctionalInterface
public interface Predicate<T> {


    /** Evaluates this predicate on the given argument. */
    boolean test (T t);
}

You might want to use this interface when you need to represent a boolean expression that uses an object of type T .

@FunctionalInterface
public interface Consumer<T> {

    /** Performs this operation on the given argument. */
    void accept(T t);
}
You might use this interface when you need to access an object of type T and perform some operations on it

@FunctionalInterface
public interface Function<T, R> {

    /** Applies this function to the given argument. */
    R apply(T t);
}
You might use this interface when you need to define a lambda that maps information from an input object to an output.

@FunctionalInterface
public interface Supplier<T> {

    /** Gets a result. */
    T get();
}

Interfaces can also have default methods (that is, a method with a body that provides some default implementation for a method in case it isn’t implemented by a class). An interface is still a functional interface if it has many default methods as long as it specifies only one abstract method.

Lambda expressions let you provide the implementation of the abstract method of a functional interface directly inline and treat the whole expression as an instance of a functional interface (more technically speaking, an instance of a concrete implementation of the functional interface). You can achieve the same thing with an anonymous inner class, although it’s clumsier.

Primitive specializations

Java 8 brings a specialized version of the functional interfaces in order to avoid autoboxing operations when the inputs or outputs are primitives. To avoid boxing, use IntPredicate, not Predicate<Integer>.

Exceptions

Note that none of the functional interfaces allow for a checked exception to be thrown. You have two options if you need a lambda expression to throw an exception: define your own functional interface that declares the checked exception, or wrap the lambda with a try/catch block (and you can re-throw runtime exception).

Restrictions on local variables  

closure is an instance of a function that can reference nonlocal variables of that function with no restrictions. For example, a closure could be passed as argument to another function. It could also access and modify variables defined outside its scope.
Now Java 8 lambdas and anonymous classes do something similar to closures: they can be passed as argument to methods and can access variables outside their scope. But they have a restriction: they can’t modify the content of local variables of a method in which the lambda is defined.
Those variables have to be implicitly final. It helps to think that lambdas close over values rather than variables. This restriction exists because local variables live on the stack and are implicitly confined to the thread they’re in. Allowing capture of mutable local variables opens new thread-unsafe possibilities, which are undesirable (instance variables are fine because they live on the heap, which is shared across threads).

Method references

Method references let you reuse existing method definitions and pass them just like lambdas. In some cases they appear more readable and feel more natural than using lambda expressions.

Lambda
Method reference equivalent
(Apple a) -> a.getWeight()
Apple::getWeight
() -> Thread.currentThread().dumpStack()
Thread.currentThread()::dumpStack
(str, i) -> str.substring(i)
String::substring
(String s) -> System.out.println(s)
System.out::println

You can think of method references as syntactic sugar for lambdas that refer only to a single method because you write less to express the same thing.

Recipe for constructing method references

There are three main kinds of method references:
  1. A method reference to a static method (for example, the method parseInt of Integer, written Integer::parseInt)
  2. A method reference to an instance method of an arbitrary type (for example, the method length of a String, written String::length)
  3. A method reference to an instance method of an existing object (for example, suppose you have a local variable expensiveTransaction that holds an object of type Transaction, which supports an instance method getValue; you can write expensiveTransaction::getValue)


Constructor references

You can create a reference to an existing constructor using its name and the keyword new as follows: ClassName::new.  It works similarly to a reference to a static method.
Examples (normally you would not use new on Strings or on Integer class!)

String s1 = new String();
Supplier<String> createNewString = String::new;
String s2 = createNewString.get();

Integer i1 = new Integer(12);
IntFunction<Integer> createNewInteger = Integer::new;
Integer i2 = createNewInteger.apply(12);



Lambdas in practice

Pear has weight property.

List<Pear> inventory = new ArrayList<>();
        inventory.addAll(Arrays.asList(new Pear(70), new Pear(135), new Pear(110)));
   
        // Use an anonymous class
        inventory.sort(new Comparator<Pear>() {
            public int compare(Pear p1, Pear p2){
                return p1.getWeight().compareTo(p2.getWeight());
        }});
   
        // Use lambda expressions
        inventory.sort((Pear p1, Pear p2) -> p1.getWeight().compareTo(p2.getWeight()));
   
        // Java compiler could infer the types of the parameters of a lambda expression by using the context in which the lambda appears
        inventory.sort((p1, p2) -> p1.getWeight().compareTo(p2.getWeight()));
   
        // Comparator has a static helper method called comparing that takes a Function extracting a Comparable key and produces a Comparator object
        inventory.sort(Comparator.comparing((a) -> a.getWeight()));
   
        // Use method references

        // import static java.util.Comparator.comparing;
        inventory.sort(comparing(Pear::getWeight));


        // chaining
        inventory.sort(comparing(Pear::getWeight).reversed());
        inventory.sort(comparing(Pear::getWeight).thenComparing(Pear::getCountry));

Ref:
Java 8 in Action book
Java 8: Behavior parameterization