pratiktest
11/17/2016 - 6:55 PM

java.md

Java learnings

  • There are two levels in java. Language level and binary level. We can code in Java 8 by setting the language level to java 8 but we can compile the code to java 7.
  • Mostly all parts are extensions of the java previous versions and can be compiled to equivalent previous java binary construct.
  • This is useful if certain library dosent work with the java 8 binary. for eg tha asm in spring 3 dosent work with java 8. If we want asm to work we need to upgrade to spring 4. But upgrading to spring 4 is not possible. So we can compile the code to java 7 by setting binary level to 7.
  • But what if we have java 8 constructs. the java compiler will try to compile them to equivalent java 7 binary construct. If it cant the compilation will fail and we will have to revert to upgrading spring.

Java 8

Optional

  • Optional is a class used to represent a value can be null or not null. This class(similar functionality) is also available in guavaa library(com.google.common.base.Optional). Its now included in java8.
  • Optional protects our code from null pointer
  • eg computer.getSoundCard().getUSB()
  • if computer dosent contain soundcard i.e soundCard is null then the above will throw null pointer exception
  • to avoid we have to do nested null checks which makes code less readable
  • You can declare Optional as follows
public class Computer {
  private Optional<Soundcard> soundcard;  
  public Optional<Soundcard> getSoundcard() { ... }
  ...
}
  • and then use it in code like below
String name = computer.flatMap(Computer::getSoundcard)
                          .flatMap(Soundcard::getUSB)
                          .map(USB::getVersion)
                          .orElse("UNKNOWN");
                          

Why is java changing

  • pressure to favour functional programming
  • process big data
  • leveraging multicore CPU

new features in java

  • Lambda, streams, default methods

Streams intro

  • Streams in Java 8 can be seen as a direct cause of the two other additions to Java 8: concise techniques to pass code to methods (method references, lambdas) and default methods in interfaces.
  • Suppose you want to write two methods that differ in only a few lines of code; you can now just pass the code of the parts that differ as an argument
  • The Java 8 feature of passing code to methods (and also being able to return it and incorporate it into data structures) also provides access to a whole range of additional techniques that are commonly referred to as functional-style programming
  • a stream is a sequence of data items that are conceptually produced one at a time
  • Unix cat creates a stream by concatenating two files, tr translates the characters in a stream, sort sorts lines in a stream, and tail -3 gives the last three lines in a stream. The Unix command line allows such programs to be linked together with pipes (|), giving examples such as
cat file1 file2  |  tr "[A-Z]"  "[a-z]"  |  sort  |  tail -3

since we have used tail lets talk about it for some time

  • tail -F filename is used to tail logs in realtime. eg as logs are built the tail command shows them

  • tail -f or --follow is used to tail logs based on file descriptor..even if file is renamed it will continue to log since it is via file descriptor.Hence if logs are written by system you can do tail -f

  • but to follow a file with filename use tail -F filename which is mostly used

  • We say that sort takes a stream of lines[3] as input and produces another stream of lines as output

  • Java 8 adds a Streams API (note the uppercase S) in java.util.stream based on this idea; Stream<T> is a sequence of items of type T. You can think of it as a fancy iterator for now.

  • The key motivation for this is that you can now program in Java 8 at a higher level of abstraction, structuring your thoughts of turning a stream of this into a stream of that

  • Java 8 can transparently run your pipeline of Stream operations on several CPU cores on disjoint parts. parallelism for free without threads

passing code intro

  • lets say we have a method sort and we have a function compareUsingYear.
  • We ususally will pass a comparator to custom sort
  • This is very verbose and if we have already written a function then why not reuse it everywhere
  • in java 8 we can pass a piece of code to sort method to do the sorting
  • no shared mutable data and the ability to pass methods and functions—code—to other methods are the cornerstones of what’s generally described as the paradigm of functional programming

Methods as first class citizens

What are first class citizens

  • In languages we manipulate values
  • like in java we will manipulate objects or primitives
  • primitives like int , String etc are first class citizens
  • classes methods are ususally considered as second class citizens

Methods as first class

  • Experiments in other languages such as Scala and Groovy have determined that allowing concepts like methods to be used as first-class values made programming easier by adding to the toolset
  • lets say we want to filter hidden file. We already have a function to do this, but we need to pass it as a fileFilter object to a list function
Files[] hiddenFiles = new File(".").listFiles(new FileFilter(){
    public boolean accept(File file){
      return file.isHidden();
    }
  });

lets talk a little about anonymous class

  • anonymous class is an expression which contains the name of the class to be subclassed and the functionality that you need to subclass
  • in above eg it is new FileFilter(){// functionality}.. filefilter is subclassed and the new functionality is provided
  • you can also assign it to a variable(object) too. assumption below is Filter is an interface or a class which is subclassed anonymously
FileFilter f = new Filter(){
  public boolean accept(File file){
      return file.isHidden();
    }
};
  • But!!! you already have a method isHidden that you could use. Why do you have to wrap it up in a verbose FileFilter class and then instantiate it
  • now in java 8 we can create a method reference like this File::isHidden which can similarly be passed around
  • so our code becomes Files[] hiddenFiles = new File(".").listFiles(File::isHidden). This is example of lamdas anonymous functions

Passing code

  • Suppose you have a class Apple with a method getColor and a variable inventory holding a list of Apples
  • you might wish to select all the green apples
  • What we are doing is filtering out green apples. The word filter is used to denote this concept
  • before java 8 you will write a method filterGreenApples. For every filter we may write a method(eg for heavyApples we may write filterHeavyApples()). This is copy paste code with a different if condition
  • In java8 a method can be passed as predicate parameter
public static boolean isGreenApple(Apple apple){
  return "green".equals(apple.getColor());
}

static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p){
  List<Apple> result = new ArrayList<>();
  for(Apple apple: inventory){
    if(p.test(apple){
      result.add(apple)
    }
  }
}

  • To use this we use filterApples(inventory, Apple::isGreenApple);
  • note a Predicate is a function that returns true or false

Methods to Lambdas

  • passing methods as predicate is fine but then we will have to write short methods like isGreenApple, isHeavyApple.. which can become lenghty
  • here we use anonymous functions or lambdas to achieve this
filterApples(inventory, (Apple a) -> "green.equals(a.getcolor()));
or
filterApples(inventory, (Apple a) -> a.getWeight() > 150);
  • so in class Apple (Apple a) create a predicate --> predicate

Streams

  • lets say... you need to filter list of transactions and then group them by currency
Map<Currency, List<Transaction>> transactionsByCurrencies =  new HashMap<>(); //we have to group the transactions in this map w,r,t currencies
for(Transaction transaction: transactions) {
  if(transaction.getPrice() > 1000) {
    Currency currency = transaction.getCurrency();
    List<Transactions> transactionForCurrency = transactionsByCurrencies.get(currency);
    if(transactionsforCurrency == null) {
      //add it in array list of transactionsByCurrencies
    }
    transactionsForCurrency.add(transaction);
  }
}
  • using streams it very less code
Map<Currency, List<Transaction>> transactionsByCurrencies = transactions.stream().filter((Transaction t) --> t.getPrice() > 1000).collect(groupingBy(Transaction::getCurrency));

  • if you see there is no foreach loop. the iteration happens inside the stram library. This is called internal iteration
  • also with streams we can process this list on multiple cores. If we have a huge list then this becomes highly efficient
  • the best way to filter a collection is to convert it into a stream filter it (here we use multicore CPU to process) and then convert it back to the collection
  • parallelism almost for free* We can use parallelStreams() method to achieve the above

Default methods

  • this feature is to write more evolvable interfaces
  • if you see this code
List<Apple> heavyApples = inventory.stream().filter((....blah)
  • However before java 8 list interface did not have streams neither filter methods so the code wont compile.
  • We could have added our own interface and implemented streams while java developers would have added it in Collections and ArrayList would implement it
  • but then all other Collections will also have to implement it
  • so how can you evolve published interfaces without disrupting exisiting implementations
  • in java 8 interfaces can have methods in which implementing class dosent have to provide implementation, but a default method in interface will provide a default implementation

Diamond Problem

  • If B and C inherit from A and D inherits B and C (multiple inheritance) it forma a diamond

    A
    

    B C

    D
    
  • if B and C overrides method from A. Which method will D receive?

  • so in java 8 if A B and C are interfaces B and C can provide different default implementation of an abstract method in A. Hence D dosent know which one to take?. So to mitigate this D should reimplimet the method or compile error is thrown

Streams

Streams but why!!

  • used for processing collections
  • We ususally want to group collections. Like select all dishes > 500. SQL provides this declarative functionality
  • howver in java we might have to iterate and then accumulate in another collection
  • Also we want to process a large collection. how would we do it fase? We need parallel processing

What are Streams

  • Streams are a way in java to manipulate data in declarative manner

IN JAVA 7

  • lets see how we can filter the dishes that are low in calories
  • We will iterate over dishes and then have an if statement to filter it
  • now we want to sort the dishes
  • so we will have a
Collections.sort(dishArrayListVariable , new Comparator{
    public int compare(Dish d1, Dish d2){
      return Integer.compare(d1.getCalories(), d2.getCalories())
    }
  }
  • now we want to only store the array of names so we iterate over the dishes again and store the names in an ArrayList
  • This process also creates lot of garbage variables whose purpose is to just be an intermediary variable for processing and later it is thrown away

IN JAVA 8


Node d is a dish

  List<String> lowCalorificDishes = 
                        menu.stream()
                              .filter(d -> d.getCalories() < 400) //select
                              .sorted(comparing(Dish::getCalories)) //comparator
                              .map(Dish::getName) //extract names
                              .collect(toList()) //convert to list

  • if you use menu.parallelStream() this will execute on multiple cores
  • grouping dishes by type
Map<Dish.Type, List<Dish>> dishesByType =
    menu.stream().collect(groupingBy(Dish::getType));
    
    Map may contain
    {FISH=[prawns, salmon],
 OTHER=[french fries, rice, season fruit, pizza],
 MEAT=[pork, beef, chicken]}

DEFINITION OF STREAMS

  • a sequence of elements from a source that supports data processing operations

Streams Functions

  • many stream operations return stream themselves allowing pipelining. like unix pipes

  • filter: takes lambda to exclude certain elements

  • map: takes lambda to transform an element into another. eg .map(Dish::getName) //extract names

  • limit: truncates a stream to contain no more than given elements

  • collect: converts a stream into another form

Difference between collection and streams

  • Collections are in memory data structures. An element has to be computed and then added to the collection. Its like a DVD. It contains the whole video or the whole data structure. You need everything before the user starts playing
  • In contrast a Stream is like streaming on internet. Its on demand data structure. For instance if we want all the prime numbers then a Collection which will give that will just run in loop since its an infinite series. But with a stream we can get the on demand prime numbers when requested by customer
  • in short streams are lazily constructed on demand
  • its similar to showing top 10 matches and then have a show more button

TRAVERSABLE ONLY ONCE ** a stream is only traversable once. After its consumed it cannot be traversed again**

** INTERNAL TRAVERSAL TO OPTIMIZE** streams have internal iteration. This enables them to manage the parallelism and order of doing things.in a foreach we will specify an order of iterating and doing things

Stream operations

  • intermidiate operations like filter, sort and limit return other streams
  • They do not do any processing until terminal operation is invoked . This is since they can be merged into single pass by terminal operation
  • eg if we have limit(3) only 1st 3 will be processed in filter or sort
  • also filter and map 2 different operations can be merged in same pass..i.e first filter 1st element then map...filter 2nd element then map..VS filter all then map all
  • terminal operations produce result from a stream pipeline. Result is nonstream value like List or Integer or even void
  • idea behind stream is similar to builder pattern.
  • Builder: chain of calls to set up configuration
  • Stream: chain of intermidiate operations followed by build command(terminal operation)

Working with Streams

  • filtering with predicate(true or false). filter tales an argument predicate to filter a stream
  • menu.stream().filter(Dish::isVegetarian).collect(toList())
  • stream also has a distict() mrthod you can chain.
  • menu.stream().distinct()
  • limit(n) truncates a stream
  • limit(n) works on unordered streams (like set) and you should not assume any order of result produced by limit
  • skip(n) method skips first n elements in a stream
  • map takes a function as argument. The function is mapped to each element in stream to convert it to new element
List<String> dishNames = menu.stream()
                             .map(Dish::getName)
                             .collect(toList());
  • becasue get name returns stream. The stream outputted by map is a string
  • return distinct words in hello world
words.stream()
     .map(word -> word.split(""))
     .distinct()
     .collect(toList());
  • the problem with above is the lambda will return String[] while we want Stream<String> so the thing inside map should return a String
  • FLAT MAP to rescue
words.stream()
     .map(word -> word.split(""))
     .map(Arrays::stream)
     .distinct()
     .collect(toList());
  • The current solution still doesn’t work! This is because you now end up with a list of streams (more precisely, Stream<Stream>)!
words.stream()
     .map(word -> word.split(""))
     .flatMap(Arrays::stream)
     .distinct()
     .collect(toList());
  • flatMap works with contents of the streams and not with the stream
  • so Arrays::stream is applied to content of String[] which is a String element in the array
  • hence we get stream<String> and not stream<String[]>
List<Integer> numbers1 = Arrays.asList(1, 2, 3);
List<Integer> numbers2 = Arrays.asList(3, 4);

List<int[]> pairs =
  numbers1.stream()
            .flatMap(i -> numbers2.stream()
                                  .map(j -> new int[]{i, j})
                    )
            .collect(toList());
  • above converts every integer in the stream to a pair

  • every number in stream i is converted to a stream

  • and then this gives us a Stream.

  • now flatmap is going to use the value in the stream which is the integer in numbers1

  • and it converts that into a pair int[]{i,j}

  • divisible by 3

 numbers1.stream()
            .flatMap(i ->
                       numbers2.stream()
                               .filter(j -> (i + j) % 3 == 0)
                               .map(j -> new int[]{i, j})
                    )
            .collect(toList());

Finding and matching in streams

  • checking to see if any predicate matches using anyMatch()
if(menu.stream().anyMatch(Dish::isVegetarian)){
    System.out.println("The menu is (somewhat) vegetarian friendly!!");
}
  • anyMatch() return a boolean and hence is a terminal operation
  • check to see if all elements match a predicate allMatch()
boolean isHealthy = menu.stream()
                        .allMatch(d -> d.getCalories() < 1000);
  • opposite of allMatch is noneMatch()
boolean isHealthy = menu.stream()
                        .noneMatch(d -> d.getCalories() >= 1000);
  • these operators follow *short circuting principle. eg(If one & is false entire is false.)
  • **findAny() returns an arbitary element. It can be used with other operatiors
Optional<Dish> dish =
  menu.stream()
      .filter(Dish::isVegetarian)
      .findAny();
  • find any returns an Optional
  • findFirst() finds the first of the stream
  • find the first square divisible by 3
List<Integer> someNumbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> firstSquareDivisibleByThree =
  someNumbers.stream()
             .map(x -> x * x)
             .filter(x -> x % 3 == 0)
             .findFirst(); // 9

Fold or Reduce operation

  • java 7 has for each
int sum = 0;
for (int x : numbers) {
    sum += x;
}
  • In above code we have sum=0 and sum = sum+x

  • in java 8 int sum = numbers.stream().reduce(0, (a, b) -> a + b);

  • reduce takes 2 arguments

  • 0 which is the initial value

  • BinaryOperator combines 2 elements and produces new value

  • the lambda combines each element repeatedly until it is reduced to a single value

int sum = numbers.stream().reduce(0, Integer::sum);

  • we can also use a method reference as above instead of lambda
  • There’s also an overloaded variant of reduce that doesn’t take an initial value, but it returns an Optional object:

Optional<Integer> sum = numbers.stream().reduce((a, b) -> (a + b));

  • since initial value is not set Optional is returned for the case where stream is null

  • we can also compute maxima and minima using reduction Optional<Integer> max = numbers.stream().reduce(Integer::max);

  • counting number of elements in a stream using map and reduce

int count = menu.stream()
                .map(d -> 1)
                .reduce(0, (a, b) -> a + b);
  • there is also a built in method count to calculate the number of elements in stream

long count = menu.stream().count();

  • The benefit of using reduce compared to the step-by-step iteration summation that you wrote earlier is that the iteration is abstracted using internal iteration, which enables the internal implementation to choose to perform the reduce operation in parallel

int sum = numbers.parallelStream().reduce(0, Integer::sum);

  • Stateful Operations

  • By contrast, some operations such as sorted or distinct seem at first to behave like filter or map—all take a stream and produce another stream (an intermediate operation), but there’s a crucial difference. Both sorting and removing duplicates from a stream require knowing the previous history to do their job. These operations are called stateful operations

  • stream also supports min and max operators that take a

  • query to find smallest transactions

Optional<Transaction> smallestTransaction =
  transactions.stream()
              .min(comparing(Transaction::getValue));

Numeric Streams

  • there is a boxing cost associated with the below stream query, where each integer has to be unboxed to primitive
int calories = menu.stream()
                   .map(Dish::getCalories)
                   .reduce(0, Integer::sum);
  • wish we had something like
int calories = menu.stream()
                   .map(Dish::getCalories)
                   .sum();
  • but map generates a Stream which may not necessarily be stream of primitives. so how can we sum them??

  • Primitive stream specializations to the rescue

  • Java 8 introduces three primitive specialized stream interfaces to tackle this issue, IntStream, DoubleStream, and LongStream

  • Each of these interfaces brings new methods to perform common numeric reductions such as sum to calculate the sum of a numeric stream and max to find the maximum element

int calories = menu.stream()
                   .mapToInt(Dish::getCalories)
                   .sum()
  • mapToInt returns an IntStream rather than Stream which prevents Unboxing

  • We can also convert this stream back to Stream

  • IntStream intStream = menu.stream().mapToInt(Dish::getCalories)

  • Stream<Integer> stream = instream.boxed()

  • to denote sum is Optional if stream is empty we have

  • a primitive specialized version of Optional as well for the three primitive stream specializations: OptionalInt, OptionalDouble, and OptionalLong.

OptionalInt maxCalories = menu.stream()
                              .mapToInt(Dish::getCalories)
                              .max();
  • suppose you’d like to generate all numbers between 1 and 100. Java 8 introduces two static methods available on IntStream and LongStream to help generate such ranges: range and rangeClosed

IntStream evenNumbers = IntStream.rangeClosed(1,100).filter(n->n%2==0)

  • generating pythogaras triplets
IntStream.rangeClosed(1, 100)
         .filter(b -> Math.sqrt(a*a + b*b) % 1 == 0)
         .boxed()
         .map(b -> new int[]{a, b, (int) Math.sqrt(a * a + b * b)});
  • Since we have used IntStream we have to use boxed since map will return another Integer(as we have intStream) but we want a Stream<Integer[]>

  • We can also use mapToObj to achieve this

IntStream.rangeClosed(1, 100)
         .filter(b -> Math.sqrt(a*a + b*b) % 1 == 0)
         .mapToObj(b -> new int[]{a, b, (int) Math.sqrt(a * a + b * b)});
  • Create a stream of Strings
Stream<String> stream = Stream.of("Java 8 ", "Lambdas ", "In ", "Action");
stream.map(String::toUpperCase).forEach(System.out::println);
  • Create an empty Stream
Stream<String> emptyStream = Stream.empty();
  • using Arrays to create stream
int sum  = Arrays.stream(numbers).sum()
  • get unique words in a file
Stream<String> lines =Files.lines(Paths.get("a.txt", Charset.defaultCharset())){
  uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(""))
                .distinct()
                .count()

Stream from functions

  • 2 static methods
    • Stream.iterate
    • Stream.generate
    • It’s generally sensible to use limit(n) on such streams to avoid printing an infinite number of values.
Stream.iterate(0, n -> n + 2)
      .limit(10)
      .forEach(System.out::println);
  • create fibonacci tuples
Stream.iterate(new int[]{0, 1},
               t -> new int[]{t[1], t[0]+t[1]})
      .limit(20)
      .forEach(t -> System.out.println("(" + t[0] + "," + t[1] +")"))
  • generate
Stream.generate(Math::random)
      .limit(5)
      .forEach(System.out::println);

Collecting data

  • Collectors are extremely useful because they provide a concise yet flexible way to define the criteria that collect uses to produce the resulting collection. More specifically, invoking the collect method on a stream triggers a reduction operation (parameterized by a Collector) on the elements of the stream itself

  • Typically, the Collector applies a transforming function to the element

List<Transaction> transactions =
    transactionStream.collect(Collectors.toList());
  • Features of collectores
    • Reducing and summarizing stream elements to a single value
    • Grouping elements
    • Partitioning elements
Comparator<Dish> dishCaloriesComparator =
  Comparator.comparingInt(Dish::getCalories);

Optional<Dish> mostCalorieDish =
  menu.stream()
      .collect(maxBy(dishCaloriesComparator));
      
  • collector is a reducing process. It is the argument in the collect() method

Sumarization

int totalCalories = menu.stream().collect(summingInt(Dish::getCalories));
double avgCalories =
    menu.stream().collect(averagingInt(Dish::getCalories));
IntSummaryStatistics menuStatistics =
        menu.stream().collect(summarizingInt(Dish::getCalories));
  • its nothing but reduction
int totalCalories = menu.stream().collect(reducing(
                                   0, Dish::getCalories, (i, j) -> i + j));
  • reduce method is meant to combine two values and produce a new one; it’s an immutable reduction

  • the collect method is designed to mutate a container to accumulate the result it’s supposed to produce

  • the collect method is useful for expressing reduction working on a mutable container but crucially in a parallel-friendly way

Grouping

  • group by
Map<Dish.Type, List<Dish>> dishesByType =
                      menu.stream().collect(groupingBy(Dish::getType));
                      
{FISH=[prawns, salmon], OTHER=[french fries, rice, season fruit, pizza],
 MEAT=[pork, beef, chicken]}
 
  • complex grouping using enum and filters
public enum CaloricLevel { DIET, NORMAL, FAT }

Map<CaloricLevel, List<Dish>> dishesByCaloricLevel = menu.stream().collect(
         groupingBy(dish -> {
                if (dish.getCalories() <= 400) return CaloricLevel.DIET;
                else if (dish.getCalories() <= 700) return
    CaloricLevel.NORMAL;
        else return CaloricLevel.FAT;
         } ));

Refactoring

  • refactor the anonymous classes to lambda expressions
Runnable r1 = new Runnable(){
  public void run(){
    System.out.println("Hello");
  }
};
Runnable r2 = () -> System.out.println("Hello");

Anonymous classes can shadow variables from the enclosing class but not lambda

int a =10
Runnable r1 = () -> {
  int a =2 //compile error
}

Problems in lambda

  • in an anonymous class this refers to the anonymous class itself but lambda it refers to the main (enclosing) class
  • overloading is a problem in lamdba

eg

interface Task {
  public void execute()
}

public static void doSomething(Runnable r){r.run()}
public static void doSomething(Task t){t.execute()};

  • so in above in anonymous function we can do following easily
doSomething(new Task(){
  public void execute() {
    System.out.println("Danger danger")
    }
  });
  • above we created a Task object for doSomething which specifically takes a task object so compiler knows which doSomething to call
  • Converting above to lambda
doSomething( () -> System.out.println("Danger danger"));
  • Here we are passing a code block to the doSomething method
  • internally this should create a object wrap it around that object and execute the interface function..but in lambda since we havent specified the object type it dosent know which object to wrap it around
  • doSomething( (Task t) -> t.execute(System.out.println("Danger danger"))
  • or doSomething((Task)() -> System.out.println("Danger danger"));