Monday, 20 February 2017

Java 8 Collection improvements

Background

With introduction of functional interfaces and Lambda expressions there are some new APIs introduced in collection class. In this post we will look at those.


Conditional removal [Collection.removeIf]

There is a new method added in Collection interface as -

  • boolean removeIf(Predicate<? super E> filter)

This basically removes elements from the list that match the predicate. No magic here if you see the default implementation it is as follows -

    default boolean removeIf(Predicate<? super E> filter) {
        Objects.requireNonNull(filter);
        boolean removed = false;
        final Iterator<E> each = iterator();
        while (each.hasNext()) {
            if (filter.test(each.next())) {
                each.remove();
                removed = true;
            }
        }
        return removed;
    }

So you basically iterate on your collection using a iterator and remove all elements that match your predicate condition. 

Let's see an example now -

        List<String> countriesList = new ArrayList<>();
        countriesList.add("India");
        countriesList.add("Srilanka");
        countriesList.add("Nepal");
        countriesList.add("Italy");
        countriesList.add("Bhutan");
        countriesList.add("Ireland");
        System.out.println("Before : " + countriesList);
        countriesList.removeIf(s -> s.startsWith("I"));
        System.out.println("After : " + countriesList);

Output is :
Before : [India, Srilanka, Nepal, Italy, Bhutan, Ireland]
After : [Srilanka, Nepal, Bhutan]

As you can see all Strings in the list starting with 'I' are removed as defined by the predicate.


Updating all elements(List.replaceAll)

Another method that is introduced is - 

  • void replaceAll(UnaryOperator<E> o)
It replaces all the elements in the current List to new values based on the UnaryOperator.  Let's revisit above example with minor modification -

        List<String> countriesList = new ArrayList<>();
        countriesList.add("India");
        countriesList.add("Srilanka");
        countriesList.add("Nepal");
        countriesList.add("Italy");
        countriesList.add("Bhutan");
        countriesList.add("Ireland");
        System.out.println("Before : " + countriesList);
        countriesList.replaceAll(s -> "Miss " + s);
        System.out.println("After : " + countriesList);


and the output is :
Before : [India, Srilanka, Nepal, Italy, Bhutan, Ireland]
After : [Miss India, Miss Srilanka, Miss Nepal, Miss Italy, Miss Bhutan, Miss Ireland]

NOTE : Recollect UnaryOperator takes a single argument of type t and returns a value of same type t. If you wish to recollect common functional interfaces check out the links in the Related links section below.

Iterating over a List (List.forEach)

Another useful method that is added in List is -
  • public void forEach(Consumer<? super E> action)
It lets you take an action for  all the elements in the List. Lets see this now.

        List<String> countriesList = new ArrayList<>();
        countriesList.add("India");
        countriesList.add("Srilanka");
        countriesList.add("Nepal");
        countriesList.add("Italy");
        countriesList.add("Bhutan");
        countriesList.add("Ireland");
        System.out.println("Before : " + countriesList);
        countriesList.forEach(System.out::println);
        System.out.println("After : " + countriesList);


and the output is -
Before : [India, Srilanka, Nepal, Italy, Bhutan, Ireland]
India
Srilanka
Nepal
Italy
Bhutan
Ireland
After : [India, Srilanka, Nepal, Italy, Bhutan, Ireland]


NOTE : Careful. Do not alter the List with the action or you will get - java.util.ConcurrentModificationException

NOTE : Above method reference System.out::println is same as saying s -> System.out.println(s)

New APIs added in Map

New APIs added -
  1. merge()
  2. putIfAbsent() : puts in map is key is not present or the value is null
  3. computeIfPresent() : calls the BiFunction when the requested key is found
  4. computeIfAbsent() : calls the BiFunction when the key isn’t present or
    is null
 Let's see merge first -

It's method is as follows -

    default V merge(K key, V value,
            BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
        Objects.requireNonNull(remappingFunction);
        Objects.requireNonNull(value);
        V oldValue = get(key);
        V newValue = (oldValue == null) ? value :
                   remappingFunction.apply(oldValue, value);
        if(newValue == null) {
            remove(key);
        } else {
            put(key, newValue);
        }
        return newValue;
    }
Above is the default implementation in Map method.  Quick observation :
  1. If old value is null, bifunction is never called. 
  2. If mapping function returns null key is removed from the Map.
 computeIfAbsent and computeIfPresent are again similar. They also take bifunction. computeIfAbsent executes the mapping function if key is not present or value is null and computeIfPresent executes the mapping function if key is present and the value is not null. Following are it's signatures -

  •     default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
  •     default V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)


Related Links


Saturday, 18 February 2017

Working with Java 8 Date/Time API

Background

Before Java 8 how do we work with Date/Time. We used java.util.Date class. Like below -

        Date d = new Date();
        System.out.println(d);
        Date cd = Calendar.getInstance().getTime();
        System.out.println(cd);
        System.out.println(d.getDate());
        System.out.println(d.getTime());
        System.out.println(System.currentTimeMillis());//same as getTime above

Output :
Sat Feb 18 10:42:17 IST 2017
Sat Feb 18 10:42:17 IST 2017
18
1487394737708
1487394737728

In Java 9 Date/Time APIs are completely revamped. Most of them are in java.time.* package. We will look at them now.

New Date/Time classes

New Date/Time classes are as follows -
  • LocalDate : Contains date. No time or timezone.
  • LocalTime : Contains time. No date or timezone.
  • LocalDateTime : Contains date and time but not the timezone.
  • ZonedDateTime : Contains date and time with the timezone.
You can test it as follows -

            System.out.println(LocalDate.now());
            System.out.println(LocalTime.now());
            System.out.println(LocalDateTime.now());
            System.out.println(ZonedDateTime.now()); 


For me the output is as follows -
2017-02-18
10:59:48.133
2017-02-18T10:59:48.133
2017-02-18T10:59:48.134+05:30[Asia/Kolkata]

NOTE : Notice how T separator is used to separate Date and time in the output of  LocalDateTime and ZonedDateTime. In the output of ZonedDateTime what you see as +5.30 is relative to GMT. So current time in Asia/Kolkata time zone is 5hours 30 minutes ahead of GMT.


More ways to create Date/Time class instances

LocalDate :
public static LocalDate of(int year, int month, int dayOfMonth)
public static LocalDate of(int year, Month month, int dayOfMonth)

NOTE : month starts with 1 (end at 12) unlike normal convention used Java which is 0 based. Also Month is an enum. You cannot compare it with int.

LocalTime :
public static LocalTime of(int hour, int minute)
public static LocalTime of(int hour, int minute, int second)
public static LocalTime of(int hour, int minute, int second, int nanos)

LocalDateTime:
public static LocalDateTime of(int year, int month, int dayOfMonth, int hour, int minute)
public static LocalDateTime of(int year, int month, int dayOfMonth, int hour, int minute, int second)
public static LocalDateTime of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanos)
public static LocalDateTime of(int year, Month month, int dayOfMonth, int hour, int minute)
public static LocalDateTime of(int year, Month month, int dayOfMonth, int hour, int minute, int second)
public static LocalDateTime of(int year, Month month, int dayOfMonth, int hour, int minute, int second, int nanos)
public static LocalDateTime of(LocalDate date, LocalTime time)

NOTE : Notice how you can can use a LocalDate and LocaleTime instance to create a LocalDateTime instance.

ZonedDateTime :
public static ZonedDateTime of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanos, ZoneId zone)
public static ZonedDateTime of(LocalDate date, LocalTime time, ZoneId zone)
public static ZonedDateTime of(LocalDateTime dateTime, ZoneId zone)

NOTE : Notice how each constructor needs a ZoneId instance.  It basically tells what time zone we are working with. You can print out all the ZoneIds available or filter it to suit your needs.

        ZoneId.getAvailableZoneIds().stream()
        .filter(z -> z.contains("Kolkata"))
        .sorted().forEach(System.out::println);
        ZoneId zone = ZoneId.of("Asia/Kolkata");
        System.out.println(zone);


And it prints :
Asia/Kolkata
Asia/Kolkata

NOTE : Note that there are no explicit constructors. You need to use static method as I have mentioned above. Also you will get DateTimeException if you pass invalid arguments

You can try :
        System.out.println(LocalDate.of(2017, 13, 21));
        System.out.println(LocalDate.of(2017, 2, 29));


You will get :

Exception in thread "main" java.time.DateTimeException: Invalid value for MonthOfYear (valid values 1 - 12): 13
    at java.time.temporal.ValueRange.checkValidValue(ValueRange.java:311)
    at java.time.temporal.ChronoField.checkValidValue(ChronoField.java:703)
    at java.time.LocalDate.of(LocalDate.java:267)
    at HelloWorld.main(HelloWorld.java:83)

and

Exception in thread "main" java.time.DateTimeException: Invalid date 'February 29' as '2017' is not a leap year
    at java.time.LocalDate.create(LocalDate.java:429)
    at java.time.LocalDate.of(LocalDate.java:269)
    at HelloWorld.main(HelloWorld.java:84)

NOTE : 2012 and 2016 are leap years.

Manipulating Date/Time

You can manipulate date/time as follows -

Using Periods

Period is well span of LocalDate. That means span consisting of Year, Month, Day, Week etc. 

Eg.

Period annually = Period.ofYears(1); // every 1 year
Period quarterly = Period.ofMonths(3); // every 3 months
Period everyTwoWeeks = Period.ofWeeks(2); // every 2 weeks
Period everyYearAndAWeek = Period.of(1, 0, 5); // every year and 5 days 


NOTE : There is no time involvement in Periods. You cannot use Period with LocalTime.Also note you cannot concatenate Period methods. Well you can technically but only last one will be picked and used since these are static methods. Eg.
Period incorrectUsage = Period.ofYears(2).ofWeeks(2); // every 2 week

Now lets try printing out a Period value -

System.out.println(Period.of(2, 12, 21));

It outputs : P2Y12M21D

Now lets analyze this string. It starts with a P denoting it's  a period. Then you have Y for years, M for months and D for day. If any of the parameters are not present then Java simply omits it (it will not say 0M).

Eg.

System.out.println(Period.of(2, 0, 21));

prints: P2Y21D
Also values greater than expected limit is fine. You can give months value as 14 and Java takes care of it.

Eg.

System.out.println(Period.of(2, 14, 21));

prints : P2Y14M21D

But while computing it will subtract 14M = 1Y and 2M. You get the point. Let's see one example of how it is used and then we move to next topic -

        LocalDate localDate = LocalDate.of(2017, 8, 2);
        System.out.println(locateDate.plus(Period.of(0, 1, 2)));


print : 2017-09-04
(Adds 0 years, 1 month and 2days to existing date.)

Also note the parameters in duration are Y,M and D. So if you give something like -

        System.out.println(Period.ofWeeks(2));

it will print : P14D

Using Duration

Same as Period but works with LocalTime rather than LocalDate. Like Period began with P Duration begins with PT (Period of time). As Period had parameters - Years,Months,Days - Y,M,D duration has Hours.Minutes,Seconds H,M,S.

Some examples -
Duration twoDays = Duration.ofDays(2); // PT48H
Duration hourly = Duration.ofHours(1); // PT1H
Duration everyTwoMinute = Duration.ofMinutes(2); // PT2M
Duration everyTwentySeconds = Duration.ofSeconds(20); // PT20S
Duration everyMilli = Duration.ofMillis(1); // PT0.001S

Duration and Period Usage 

Do not mix Period with Time and Duration with Date. Period is intended to be used with Date and Duration with Time. Refer following picture for reference.

Working with Instants

Instant class represents a specific moment in time in terms of GMT time zone. Simple example -

        Instant ins1 = Instant.now();

        Thread.sleep(1000);

        Instant ins2 = Instant.now();

        System.out.println(Duration.between(ins1, ins2));

and it prints : PT1.001S
That's a second and some time needed for processing.  You cannot use Instant with LocalDate or LocalTime or LocalDateTime. It has to be ZonedDateTime since it is associated with a time zone.


Related Links


Sunday, 12 February 2017

Understanding JDBC and building database applications with it

Background

JDBC stands for Java database connectivity. This includes connecting to DB in Java, running queries and processing results.

A relational DB that has tables consisting of rows and columns. You can interact with a relational DB with -
  • JDBC APIs. You get a connection, create a statement and get result set of the query.
  • Use JPA(Java persistence API). This uses a concept called ORM (Object relational mapping) where you map Java objects to tables and operate on these objects. For eg. hibernate is one such framework.
  • SQL (Structured query language) is used to interact with the relational DB. 
 In this post we are going to understand JDBC.

Interfaces in JDBC

All JDBC classes in Java are part of java.sql.* package.  There are 4 important interfaces that you need to understand -
  1. Driver : Know how to get connection from DB
  2. Connection : Knows how to interact with DB
  3. Statement : Knows how to run SQL on the DB
  4. ResultSet : Knows the result returned by the SQL query from the DB.
 To see a sample Java code on how to connect to a DB from Java you can see one of my previous posts -
Above code snippet uses mysql DB but you can use any DB really. There are some common things that we sill see in a moment.

NOTE : You no longer have to explicitly load the driver class using Class.ForName(driver). From JDBC 4 driver class is automatically loaded from the class path. 

Building DB application with JDBC

Let's start by looking at how JDBC url looks like and is constructed -




 As you can see JDBC URL is split into 3 parts -
  1. 1st part is the jdbc protocol
  2. 2nd part is the name of the DB. For eg. mysql, postgres, derby or oracle
  3. 3rd part is respective DB specific format
You have already seem mysql connection string in code above -
  • jdbc:mysql://localhost:3306/testDB
 Some other examples are -
  • jdbc:postgresql://localhost/testDB
  • jdbc:oracle:thin:@192.168.1.45:1699:testDB
  • jdbc:derby:testDB
 Once you know the URL first step is to load the DB specific driver. As mentioned before traditionally you needed to explicitly load the driver using -
  • Class.ForName(driver)
But since JDBC 4 you don't. Java loads automatically for you if it's present in the classpath.  Once driver is loaded next you need to get the Connection from it. You can do so with -
  • Connection conn = DriverManager.getConnection("jdbc:derby:testDB");
 NOTE : If you get exception like "java.sql.SQLException: No suitable driver found for..." then the driver is not present on the classpath. Add it.

Once you have the connection you can get the Statement from it as follows -
  • Statement stmt = conn.createStatement();
  Once you have the statement you are all set to execute queries on DB -
  • ResultSet rs = stmt.executeQuery("select * from countries");
  • int res = stmt.executeUpdate("insert into countries values(1, 'India')");
 NOTE : ResultSet points to a location before 1st row when it is result. To access the data you need to call rs.next() which returns a boolean stating if more result is present. If it does you can access it via rs.getInt(1) etc.

NOTE : Column indexes start with 1. So something like rs.getInt(0)will throw SQL exception.

Once you have processed the result set never forget to close the resources and that include your -
  • ResultSet
  • Statement
  • Connection
NOTE : It is very important to close resources in the right order. If you don't want to close it manually you can always use it under try with resource statements so that Java closes them for you. If doing manually you can close it in finally statement with null checks.


Why do we use a DataSource instead of a DriverManager?

 Always use a datasource over DriverManager as-

  • Client app need not know about the DB details, username , password. App server will take care of it. With datasource all you need is a jndi name properties of which can be configured at app level.
  • App server takes care of creating and closing connections. You don’t have to manage it in your client application.
  • Data source has support for creating pool of connection whereas data manager does not.


  •  Why do we use a PreparedStatement instead of a Statement

    You should always use PreparedStatement instead of a Statement. PreparedStatement is subclass of Statement. This has multiple reasons -
    • Performance: A PreparedStatement figures out a plan to run the SQL and remembers it. Helps when same query is run multiple times.
    • Security: To prevent SQL injection. It's a famous hacking technique. Go ahead read it up on google.
    • Readability: No String concatenations in building queries.

    Related Links

    Walking a directory using Streams API in java8

    Background

    In one of the previous posts we saw how we can traverse a directory in Java as part of NIO.2 APIs introduced in Java 8 using walkFileTree method and Path and FileVisitor arguments.
     In this post we will try to do the same thing but with stream APIs introduced in Java8.

    Walking a directory using Streams API

    In this we will use Files.walk(path) method that returns a Stream<Path> instance and traverse the directories in dept first pattern. This is lazy implementation which mean child directories are not actually loaded until it's parent is traversed.

    In previous post we say how we can filter .java files using PathMatcher interface. In this post we will see how easy it is with Streams.

            Path path = Paths.get("/Users/athakur/Desktop/testDir");
            
            try {
                Files.walk(path)
                .filter(p -> p.toString().endsWith(".java"))
                .forEach(System.out::println);
                } catch (IOException e) {
                    e.printStackTrace();
                }
    

    and the output is : 
    /Users/athakur/Desktop/testDir/innerTestDir1/test2.java
    /Users/athakur/Desktop/testDir/innerTestDir1/test3.java
    /Users/athakur/Desktop/testDir/innerTestDir2/test4.java
    /Users/athakur/Desktop/testDir/test1.java

    File structure is as follows -
    • testDir
      • test1.java
      • innerTestDir1
        • test2.java
        • test3.java
      • innerTestDir2
        • test4.java
    As you can see it does a depth first search.

    NOTE : By default the directories depth searched is Integer.MAX_VALUE. Keeping it default and having a deep/large directory structure may take a lot of time to search. So the walk method has an overloaded method that takes in integer parameter (walk(Path,int)) denoting dept of directories to be searched.

    NOTE : Unlike earlier NIO.2 methods walk by default will not traverse symbolic links. This is to avoid traversing unnecessary paths or cyclic paths. But if you do wish to track symbolic links you can provide FOLLOW_LINKS option as a vararg to the walk() method. However you should provide a depth is this case to avoid unnecessary traversals. Also walk method keeps track of path traversed and if t detects a loop it will throws FileSystemLoopException.

    Files class has other helpful methods as well for searching, listing and printing files -

            try {
    
                System.out.println("Printing Java regular files");
                Files.find(path, 10, (p, a) -> p.toString().endsWith(".java") && a.isRegularFile())
                        .forEach(System.out::println);
                System.out.println("Printing non directories files");
                // traverses only 1 depth
                Files.list(path).filter(p -> !Files.isDirectory(p)).map(p -> p.toAbsolutePath())
                        .forEach(System.out::println);
    
            } catch (IOException e) {
                e.printStackTrace();
            }
           
    


    Output :
    Printing Java regular files
    /Users/athakur/Desktop/testDir/innerTestDir1/test2.java
    /Users/athakur/Desktop/testDir/innerTestDir1/test3.java
    /Users/athakur/Desktop/testDir/innerTestDir2/test4.java
    /Users/athakur/Desktop/testDir/test1.java
    Printing non directories files
    /Users/athakur/Desktop/testDir/test1.java

    NOTE : Notice how Files.list() traverses only 1 depth unlike Files.find() which traverses till the depth provided. Also notice how each method throws IOException since file may not actually be present.

    Following picture shows difference between legacy file APIs and NIO2 APIs -




    Related Links

    Sunday, 29 January 2017

    Java 8 Stream terminal operations - reduce vrs collect

    Background

    Following are some common terminal operations supported by Stream -


    In this post we will see two of these - reduce() and collect(). As you can see they are reduction. They reduce stream to an object. Lets see each of them is detail now.


    reduce()

    reduce() method combines the stream into a single object. It can reduce the stream either to same same type as that of stream or different. Methods available for reduce are -

    • T reduce(T identity, BinaryOperator<T> accumulator)
    • Optional<T> reduce(BinaryOperator<T> accumulator)
    • <U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
    Let's see some examples - 

            Stream<String> myStringStream = Stream.of("a","n","i","k","e","t");
            String stringResult = myStringStream.reduce("", (a,b) -> a + b);
            System.out.println("String reduce : " + stringResult);
            Stream<Integer> myIntegerStream = Stream.of(2,3,4,5,6);
            int intResult = myIntegerStream.reduce(1, (a,b) -> a * b);
            System.out.println("Intger reduce : " + intResult);
    

    Output is -
    String reduce : aniket
    Intger reduce : 720

    As you can see the 1st method gets the identity, then uses the 1st element of the stream and operates both to get a result. Then it takes the result and the 2nd element to process again and so on to finally return a result.

    2nd method does not take an identity as an input well because it's not strictly mandatory but if you notice it returns an Optional value. There can be 3 cases here -
    1. If the stream is empty, an empty Optional is returned.
    2. If the stream has one element, it is returned.
    3. If the stream has multiple elements, the accumulator is applied to combine them.
    For eg -

            Stream<Integer> myIntegerStream = Stream.of(2,3,4,5,6);
            Optional<Integer> intResult = myIntegerStream.reduce((a,b) -> a * b);
            if(intResult.isPresent()) {
                System.out.println("Intger reduce : " + intResult.get());
            }
    

    And the output is again -
    Intger reduce : 720

    3rd method is used mainly when parallel Streams are involved. In that case you stream is divided into segments, accumulator is used to combine individual segments and then a combiner is used to combine those segments.

    For reduce arguments to be used for parallel streams it must satisfy following properties -

    • The identity must be defined such that for all elements in the stream u ,
      combiner.apply(identity, u) is equal to u .
    • The accumulator operator op must be associative and stateless such that (a op b) op c is equal to a op (b op c) .
    •  The combiner operator must also be associative and stateless and compatible with the identity, such that for all u and t combiner.apply(u,accumulator.apply(identity,t)) is equal to accumulator.apply(u,t) .
    NOTE : As part of the parallel process, the identity is applied to
    multiple elements in the stream, resulting in very unexpected data. So above properties should be obeyed.

    collect()

    collect() is again a reduction called mutable reduction. In this we use mutable objects like StringBuilder or ArrayList to collect data. Note the result here is different type than that of the stream content. Methods available are -

    • <R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner)
    • <R,A> R collect(Collector<? super T, A,R> collector)
     For eg.

            Stream<String> myStringStream = Stream.of("a","n","i","k","e","t");
            StringBuilder stringResult = myStringStream.collect(StringBuilder::new, StringBuilder::append,StringBuilder::append);
            System.out.println("String reduce : " + stringResult.toString());
            myStringStream = Stream.of("a","n","i","k","e","t");
            TreeSet stringTreeSetResult = myStringStream.collect(TreeSet::new, TreeSet::add,TreeSet::addAll);
            System.out.println("String reduce : " + stringTreeSetResult);
    


    And the output is -
    String reduce : aniket
    String reduce : [a, e, i, k, n, t]

    Or you can use the collectors -

            Stream<String> myStringStream = Stream.of("a","n","i","k","e","t");
            List<String> resultList = myStringStream.collect(Collectors.toList());
            System.out.println(resultList);
    


    Output -
    [a, n, i, k, e, t]

    For using collect() on parallel streams make sure your mutable container is thread safe. You can use concurrent collections for this.

    Difference between reduce() and collect()

    •  If you have immutable values such as ints,doubles,Strings then normal reduction works just fine. However, if you have to reduce your values into say a List (mutable data structure) then you need to use mutable reduction with the collect method.
    • In the case of reduce() we apply the function to the stream elements themselves where as in the case of collect() we apply the function to a mutable container.

    Related Links

    t> UA-39527780-1 back to top