Sunday 26 February 2017

Working with primitives in Streams

Background

In one of the previous posts on Understanding Java 8 Stream API we saw basics of working with primitives. To reiterate - there are 3 types of primitive streams -
  1. IntStream: Used for the primitive types int, short, byte, and char
  2. LongStream: Used for the primitive type long
  3. DoubleStream: Used for the primitive types double and float
These primitives are special they got their own Optional classes , their own functional interfaces. With our previous understanding in place we will see this new working with primitives.


Why primitive Streams?

Lets say we have a list of integers and we want to compute its average. How would you do it with streams.

    public static void main(String[] args) {
        List<Integer> myIntList = Arrays.asList(1,2,3,4,5);
        Optional<Integer> sum = myIntList.stream().reduce((x,y) -> x + y);
        long count  = myIntList.stream().count();
        double avg = sum.get()/count;
        System.out.println(avg);
    }

Output : 3.0

Well sure based on what we know so far we aced this. But do you see any drawback in above approach?
  • You are creating stream twice from the list - once to get sum and once to get count.
  • Then you are dividing it to get the average.
Though this works Java provides more optimized solution and this is where IntStream comes into picture.

    public static void main(String[] args) {
        List<Integer> myIntList = Arrays.asList(1,2,3,4,5);
        OptionalDouble intAvg = myIntList.stream().mapToInt(x->x).average();
        System.out.println(intAvg.getAsDouble());
    }


Output : 3.0

That's the one liner with IntStream. mapToInt() intermediate operation gives you a IntStream which has predefined operations like sum(), average() etc. Couple of points to note -
  • mapToInt() intermediate method returned IntStream
  •  average() terminal operation on IntStream returned a OptionalDouble. This is a new class. Notice it is not Optional<Double> but entirely a new class. 
  • Also note the method to get double value from  OptionalDouble class. It is getAsDouble() and not get() like you would have seen in Optional class.
  • Recollect out discussion before primitives have their own streams eg. IntStream , own Optional classes eg. OptionalInt and even functional interfaces eg. BooleanPredicate. But that's for later.
  • sum() on IntStream returns int instead of OptionalInt because if there are no elements in the Stream it would return 0.
    public static void main(String[] args) {
        List<Integer> myIntList = Arrays.asList(1,2,3,4,5);
        int sum = myIntList.stream().mapToInt(x->x).sum();
        System.out.println(sum);
    } 


Output : 15

NOTE : If you want Object stream back from primitive streams you can use mapToObj intermediate method.

Creating primitive streams

Now that we have basics out of our way let see how can we create a primitive stream. One method we already saw by using mapToInt() method on a normal Stream. Similarly you have mapToLong() and mapToDouble() methods to get LongStream and DoubleStream respectively. 

Other normal methods that we say in Stream class like of() or generate() or iterate() still apply to primitive streams as well.

    public static void main(String[] args) {
        System.out.println("of");
        IntStream.of(1,2,3,4,5).forEach(System.out::print);
        System.out.println("\ngenerate");
        IntStream.generate(() -> 1).limit(4).forEach(System.out::print);
        System.out.println("\niterate");
        IntStream.iterate(1,(x) -> x+1).limit(4).forEach(System.out::print);
    }

Output :
of
12345
generate
1111
iterate
1234

Some other methods are 
  • range(int startInclusive, int endExclusive)
  • rangeClosed(int startInclusive, int endInclusive)
    public static void main(String[] args) {
        System.out.println("range");
        IntStream.range(1, 5).forEach(System.out::print);
        System.out.println("\nrangeClosed");
        IntStream.rangeClosed(1, 5).forEach(System.out::print);
    } 


Output :
range
1234
rangeClosed
12345

Example demonstrates for InStream but same applies for LongStream and DoubleStream as well.

NOTE : Notice the range limits. For range end limit is exclusive where as for rangeClosed it is inclusive.


Understanding summaryStatistics

Now lets say we have an IntStream and we need to to output difference between the max element and min element in the stream. How would you do that?

    public static void main(String[] args) {
        List<Integer> myList = Arrays.asList(1,2,3,4,5);
        int max = myList.stream().mapToInt(x -> x).max().getAsInt();
        int min = myList.stream().mapToInt(x -> x).min().getAsInt();
        int difference = max - min;
        System.out.println("Difference : " + difference);
    }

Output :
Difference : 4
which is expected since 5-1 = 4.
But again we need to create stream twice and need two terminal operations to get result. Java helps us here too with summaryStatistics method.

    public static void main(String[] args) {
        List<Integer> myList = Arrays.asList(1,2,3,4,5);
        IntSummaryStatistics intSummaryStatistics = myList.stream().mapToInt(x -> x).summaryStatistics();
        int difference = intSummaryStatistics.getMax() - intSummaryStatistics.getMin();
        System.out.println("Difference : " + difference);
    }

Output :
Difference : 4

Related Links

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 8 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.

NOTE : Instant is related to ZonedDateTime but it always corresponds to GMT i.e point in time as per GMT.

Accounting for Daylight Savings Time

Daylight savings time is obeserved in some countries. In this clock is adjusted by an hour twice a year to make better use of the sunlight. This transition happens on Weekend Sunday 2 AM. So when clock is at 2 AM it is moved to 3 AM.

Lets say June 29, 2017, is the weekend that clocks spring ahead for daylight savings time. Now consider following code -

LocalDate date = LocalDate.of(2017, Month.JUNE, 29);
LocalTime time = LocalTime.of(1, 30);
ZoneId usZone = ZoneId.of("US/Eastern");
ZonedDateTime zonedDateTime1 = ZonedDateTime.of(date, time, usZone);
ZonedDateTime zonedDateTime2 = dateTime1.plus(1, ChronoUnit.HOURS);
long hours = ChronoUnit.HOURS.between(zonedDateTime1, zonedDateTime2);
int hours1 = dateTime1.getHour();
int hours2 = dateTime2.getHour();
System.out.println(hours + "," + hours1 + "," + hours2);

This will output 1,1,3. While the values are two hours apart, the time zone offset changes as well, making it only change from 6:30 GMT to 7:30 GMT.

NOTE : Java is smart enough to adjust for daylight savings time. So you don't have to worry about handling it in code. Logic may differ but you don't have to worry that it might cause exception or error.

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.
      • Databases like sql server or oracle databases  have execution plan cache. They can resue the execution plan while running the same query again. This saved efforts in rebuilding execution plan. However this will work only when the sql query is exactly the same. If it has different values than db treats it as different query. In case of Prepared statements in which we use bind parameters we use placeholders so that the query remains the same and subsequently the execution plan. Bind parameters are hidden to the database query optimizer.
    • 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

    t> UA-39527780-1 back to top