Using the Comparator class in Java 8 to compare objects

Java 8 comes with a range of built-in implementations of the Comparator interface.

Consider the following Employee class:

public class Employee
{
    private UUID id;
    private String name;
    private int age;

    public Employee(UUID id, String name, int age)
    {
        this.id = id;
        this.name = name;
        this.age = age;
    }
        
    public UUID getId()
    {
        return id;
    }

    public void setId(UUID id)
    {
        this.id = id;
    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }    
    
    public int getAge()
    {
        return age;
    }

    public void setAge(int age)
    {
        this.age = age;
    }    
}

…and the following list of employees:

Read more of this post

Exploring a directory with the Java 8 Stream API

We saw an example of using the Java 8 Stream API in File I/O in this post. We saw how the Files object was enhanced with the lines() method to open a line reader stream to a text file.

There are other enhancements related to streams that make is simple to explore a directory on your hard drive. The following code example will collect all folders and files within the c:\gitrepos folder and add them to an ArrayList:

Path gitReposFolderPath = Paths.get("c:\\gitrepos");
gitReposFolderPath.toFile().getName();
try (Stream<Path> foldersWithinGitReposStream = Files.list(gitReposFolderPath))            
{
    List<String> elements = new ArrayList<>();
    foldersWithinGitReposStream.forEach(p -> elements.add(p.toFile().getName()));            
    System.out.println(elements);
}
catch (IOException ioe)
{

}

I got the following output:

[cryptographydotnet, dotnetformsbasedmvc5, entityframeworksixdemo, owinkatanademo, signalrdemo, singletondemoforcristian, text.txt, webapi2demo, windowsservicedemo]

The code returns both files and folders one level below the top directory, i.e. the “list” method does not dive into the subfolders. I put a text file into the folder – text.txt – just to test whether in fact all elements are returned.

Say you only need files – you can use the filter method:

foldersWithinGitReposStream.filter(p -> p.toFile().isFile()).forEach(p -> elements.add(p.toFile().getName())); 

This will only collect text.txt.

Let’s try something slightly more complex. We’ll organise the elements within the directory into a Map of Boolean and List of Paths. The key indicates whether the group of files are directories or not. We can use the collect method that we saw in this post:

try (Stream<Path> foldersWithinGitReposStream = Files.list(gitReposFolderPath))            
{
    Map<Boolean, List<Path>> collect = foldersWithinGitReposStream.collect(Collectors.groupingBy(p -> p.toFile().isDirectory()));
    System.out.println(collect);
}

This prints the following:

{false=[c:\gitrepos\text.txt], true=[c:\gitrepos\cryptographydotnet, c:\gitrepos\dotnetformsbasedmvc5, c:\gitrepos\entityframeworksixdemo, c:\gitrepos\owinkatanademo, c:\gitrepos\signalrdemo, c:\gitrepos\singletondemoforcristian, c:\gitrepos\webapi2demo, c:\gitrepos\windowsservicedemo]}

So we successfully grouped the paths.

As mentioned above the “list” method goes only one level deep. The “walk” method in turn digs deeper and extracts sub-directories as well:

try (Stream<Path> foldersWithinGitReposStream = Files.walk(gitReposFolderPath))
{
    List<String> elements = new ArrayList<>();
    foldersWithinGitReposStream.filter(p -> p.toFile().isFile()).forEach(p -> elements.add(p.toFile().getAbsolutePath()));
    System.out.println(elements);
}

We can also instruct the walk method to go n levels down with an extra integer argument:

try (Stream<Path> foldersWithinGitReposStream = Files.walk(gitReposFolderPath, 3))

View all posts related to Java here.

Waiting for background tasks to finish using the CountDownLatch class in Java

Imagine the situation where you execute a number of long running methods. Also, let’s say that the very last time consuming process depends on the previous processes, let’s call them prerequisites. The dependence is “sequential” meaning that the final stage should only run if the prerequisites have completed and returned. The first implementation may very well be sequential where the long running methods are called one after the other and each of them blocks the main thread.

However, in case the prerequisites can be executed independently then there’s a much better solution: we can execute them in parallel instead. Independence in this case means that prerequisite A doesn’t need any return value from prerequisite B in which case parallel execution of A and B is not an option.

In this post we’ll examine this situation and see how to implement it in Java using the CountDownLatch class.

Read more of this post

Reading text files using the Stream API in Java 8

We discussed the Java 8 Stream API thoroughly on this blog starting here. We mostly looked at how the API is applied to MapReduce operations to analyse data in a stream.

The same API can be applied to File I/O. Java 8 adds a new method called “lines” to the BufferedReader object which opens a Stream of String. From then on it’s just standard Stream API usage to filter the lines in the file – and perform other operations on them in parallel such as filtering out the lines that you don’t need.

Here’s an example how you can read all lines in a file:

String filename = "c:\\logs\\log.txt";
File logFile = new File(filename);
try (BufferedReader reader = new BufferedReader(new FileReader(logFile));)
{
    StringBuilder fileContents = new StringBuilder();
    Stream<String> fileContentStream = reader.lines();
    fileContentStream.forEach(l -> fileContents.append(l).append(System.lineSeparator()));
    System.out.println(fileContents.toString());
}
catch (IOException ioe)
{

}

We simply append each line in the stream to a StringBuilder.

In my case the log.txt file has the following contents:

hello
this is a line
next line
this is another line
…and this is yet another line
goodbye

As we’re dealing with the Stream API the usual Map, Filter and Reduce methods are all available to be performed on the text. E.g. what if you’re only interested in those lines that start with “this”? Easy:

fileContentStream.filter(l -> l.startsWith("this"))
                    .forEach(l -> fileContents.append(l).append(System.lineSeparator()));

The StringBuilder will now only hold the following lines:

this is a line
this is another line

You can also use the Path and Files objects that were introduced in Java 7. The Files object too was extended with a method to get hold of the Stream object. The below example is equivalent to the above:

Path logFilePath = Paths.get("C:\\logs\\log.txt");
try (Stream<String> fileContentStream = Files.lines(logFilePath))            
{
    StringBuilder fileContents = new StringBuilder();
    fileContentStream.filter(l -> l.startsWith("this"))
            .forEach(l -> fileContents.append(l).append(System.lineSeparator()));
    System.out.println(fileContents.toString());
}
catch (IOException ioe)
{

}

View all posts related to Java here.

Insert a non-existent value into a Map in Java 8

Consider the following Employee class:

public class Employee
{
    private UUID id;
    private String name;
    private int age;

    public Employee(UUID id, String name, int age)
    {
        this.id = id;
        this.name = name;
        this.age = age;
    }
        
    public UUID getId()
    {
        return id;
    }

    public void setId(UUID id)
    {
        this.id = id;
    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }    
    
    public int getAge()
    {
        return age;
    }

    public void setAge(int age)
    {
        this.age = age;
    }
}

Let’s put some Employee objects into a hash map:

Read more of this post

Adjusting the date in Java 8 Date and Time API

Introduction

We saw a couple of new concepts in the Java 8 Date and Time API on this blog:

All the above classes expose methods called “with” with a couple of overloads. LocalDate, LocalTime and LocalDateTime come with other methods whose names start with “with”, such as withSeconds or withMonth depending on the supported level of time unit. The “with” methods adjust some value of the date-related instances and return a new instance.

Read more of this post

Return a default value from a Map in Java 8

Consider the following Employee class:

public class Employee
{
    private UUID id;
    private String name;
    private int age;

    public Employee(UUID id, String name, int age)
    {
        this.id = id;
        this.name = name;
        this.age = age;
    }
        
    public UUID getId()
    {
        return id;
    }

    public void setId(UUID id)
    {
        this.id = id;
    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }    
    
    public int getAge()
    {
        return age;
    }

    public void setAge(int age)
    {
        this.age = age;
    }
}

Let’s put some Employee objects into a hash map:

Read more of this post

How to merge two Maps in Java 8

The Map interface has been extended with the “merge” function in Java 8. Let’s see an example on how to use it.

Consider the following Empolyee class:

public class Employee
{
    private UUID id;
    private String name;
    private int age;

    public Employee(UUID id, String name, int age)
    {
        this.id = id;
        this.name = name;
        this.age = age;
    }
        
    public UUID getId()
    {
        return id;
    }

    public void setId(UUID id)
    {
        this.id = id;
    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }    
    
    public int getAge()
    {
        return age;
    }

    public void setAge(int age)
    {
        this.age = age;
    }
}

Let’s say that we have the following two maps where the key is an indicator of the employees’ performance and the value is the list of employees that fall into that category:

Read more of this post

Default interface functions in Java 8

Introduction

A new feature in Java 8 is default function implementations. They are default implementations of methods of an interface. Default methods can help extending an interface without breaking the existing implementations. After all if you add a new method to an interface then all implementing types must handle it otherwise the compiler will complain.

This can be cumbersome if your interface has a large number of consumers. You’ll break their code and they will need to implement the new function – which they might not even need.

Read more of this post

Formatting dates in Java 8 using DateTimeFormatter

Introduction

Formatting dates – and numbers for that matter – can be a complex matter. The DateTimeFormatter class provides pre-defined formats that adhere to ISO and RCF specifications.

DateTimeFormatter

The following date related classes we’ve seen on this blog, i.e.

…have a method called “format” which accepts a DateTimeFormatter class. This class has a number of pre-defined formats that you can readily use in your application. Note that not all such formats will be available. The availability depends on the exact object type of the date class. E.g. ISO_ZONED_DATE_TIME won’t work with LocalDateTime as it is meant to format zoned dates. Also, ISO_DATE_TIME won’t be available for the LocalDate class as it has no concept of time units below the level of days.

Let’s test all the predefined values with the above date types.

LocalDate

We run the following code and check the output:

System.out.println("Running example with LocalDate class.");
LocalDate now = LocalDate.now();
        try
        {
            System.out.println("ISO_DATE: " + now.format(DateTimeFormatter.ISO_DATE));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("ISO_DATE is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("BASIC_ISO_DATE: " + now.format(DateTimeFormatter.BASIC_ISO_DATE));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("BASIC_ISO_DATE is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("ISO_DATE_TIME: " + now.format(DateTimeFormatter.ISO_DATE_TIME));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("ISO_DATE_TIME is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("ISO_INSTANT: " + now.format(DateTimeFormatter.ISO_INSTANT));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("ISO_INSTANT is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("ISO_LOCAL_DATE: " + now.format(DateTimeFormatter.ISO_LOCAL_DATE));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("ISO_LOCAL_DATE is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("ISO_LOCAL_DATE_TIME: " + now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("ISO_LOCAL_DATE_TIME is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("ISO_LOCAL_TIME: " + now.format(DateTimeFormatter.ISO_LOCAL_TIME));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("ISO_LOCAL_TIME is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("ISO_OFFSET_DATE: " + now.format(DateTimeFormatter.ISO_OFFSET_DATE));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("ISO_OFFSET_DATE is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("ISO_OFFSET_DATE_TIME: " + now.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("ISO_OFFSET_DATE_TIME is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("ISO_OFFSET_TIME: " + now.format(DateTimeFormatter.ISO_OFFSET_TIME));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("ISO_OFFSET_TIME is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("ISO_ORDINAL_DATE: " + now.format(DateTimeFormatter.ISO_ORDINAL_DATE));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("ISO_ORDINAL_DATE is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("ISO_TIME: " + now.format(DateTimeFormatter.ISO_TIME));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("ISO_TIME is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("ISO_WEEK_DATE: " + now.format(DateTimeFormatter.ISO_WEEK_DATE));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("ISO_WEEK_DATE is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("ISO_ZONED_DATE_TIME: " + now.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("ISO_ZONED_DATE_TIME is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("RFC_1123_DATE_TIME: " + now.format(DateTimeFormatter.RFC_1123_DATE_TIME));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("RFC_1123_DATE_TIME is not supported: " + e.getMessage());
        }

We get the following output:

Running example with LocalDate class.
ISO_DATE: 2014-11-01
BASIC_ISO_DATE: 20141101
ISO_DATE_TIME is not supported: Unsupported field: HourOfDay
ISO_INSTANT is not supported: Unsupported field: InstantSeconds
ISO_LOCAL_DATE: 2014-11-01
ISO_LOCAL_DATE_TIME is not supported: Unsupported field: HourOfDay
ISO_LOCAL_TIME is not supported: Unsupported field: HourOfDay
ISO_OFFSET_DATE is not supported: Unsupported field: OffsetSeconds
ISO_OFFSET_DATE_TIME is not supported: Unsupported field: HourOfDay
ISO_OFFSET_TIME is not supported: Unsupported field: HourOfDay
ISO_ORDINAL_DATE: 2014-305
ISO_TIME is not supported: Unsupported field: HourOfDay
ISO_WEEK_DATE: 2014-W44-6
ISO_ZONED_DATE_TIME is not supported: Unsupported field: HourOfDay
RFC_1123_DATE_TIME is not supported: Unsupported field: HourOfDay

LocalTime

We run the same code as above but change the class type:

System.out.println("Running example with LocalTime class.");
LocalTime now = LocalTime.now();

…and here’s the output, again with Locale set to Sweden:

Running example with LocalTime class.
ISO_DATE is not supported: Unsupported field: Year
BASIC_ISO_DATE is not supported: Unsupported field: Year
ISO_DATE_TIME is not supported: Unsupported field: Year
ISO_INSTANT is not supported: Unsupported field: InstantSeconds
ISO_LOCAL_DATE is not supported: Unsupported field: Year
ISO_LOCAL_DATE_TIME is not supported: Unsupported field: Year
ISO_LOCAL_TIME: 22:02:52.932
ISO_OFFSET_DATE is not supported: Unsupported field: Year
ISO_OFFSET_DATE_TIME is not supported: Unsupported field: Year
ISO_OFFSET_TIME is not supported: Unsupported field: OffsetSeconds
ISO_ORDINAL_DATE is not supported: Unsupported field: Year
ISO_TIME: 22:02:52.932
ISO_WEEK_DATE is not supported: Unsupported field: WeekBasedYear
ISO_ZONED_DATE_TIME is not supported: Unsupported field: Year
RFC_1123_DATE_TIME is not supported: Unsupported field: DayOfMonth

Most formats are not supported by LocalTime as there’s no notion of days, months etc. in that object.

LocalDateTime

Run the same code with “now” set as follows:

System.out.println("Running example with LocalDateTime class.");
LocalDateTime now = LocalDateTime.now();

…and I got the following output:

Running example with LocalTime class.
ISO_DATE: 2014-11-01
BASIC_ISO_DATE: 20141101
ISO_DATE_TIME: 2014-11-01T22:07:24.329
ISO_INSTANT is not supported: Unsupported field: InstantSeconds
ISO_LOCAL_DATE: 2014-11-01
ISO_LOCAL_DATE_TIME: 2014-11-01T22:07:24.329
ISO_LOCAL_TIME: 22:07:24.329
ISO_OFFSET_DATE is not supported: Unsupported field: OffsetSeconds
ISO_OFFSET_DATE_TIME is not supported: Unsupported field: OffsetSeconds
ISO_OFFSET_TIME is not supported: Unsupported field: OffsetSeconds
ISO_ORDINAL_DATE: 2014-305
ISO_TIME: 22:07:24.329
ISO_WEEK_DATE: 2014-W44-6
ISO_ZONED_DATE_TIME is not supported: Unsupported field: OffsetSeconds
RFC_1123_DATE_TIME is not supported: Unsupported field: OffsetSeconds

ZonedDateTime

This is probably the most interesting case as it supports all the predefined formats in DateTimeFormatter. Let’s look at an example with Australia/Adelaide:

System.out.println("Running example with ZonedDateTime class.");
ZoneId brisbane = ZoneId.of("Australia/Adelaide");
ZonedDateTime now = ZonedDateTime.of(LocalDateTime.now(), brisbane);

Running example with ZonedDateTime class.
ISO_DATE: 2014-11-01+10:30
BASIC_ISO_DATE: 20141101+1030
ISO_DATE_TIME: 2014-11-01T22:13:48.87+10:30[Australia/Adelaide]
ISO_INSTANT: 2014-11-01T11:43:48.870Z
ISO_LOCAL_DATE: 2014-11-01
ISO_LOCAL_DATE_TIME: 2014-11-01T22:13:48.87
ISO_LOCAL_TIME: 22:13:48.87
ISO_OFFSET_DATE: 2014-11-01+10:30
ISO_OFFSET_DATE_TIME: 2014-11-01T22:13:48.87+10:30
ISO_OFFSET_TIME: 22:13:48.87+10:30
ISO_ORDINAL_DATE: 2014-305+10:30
ISO_TIME: 22:13:48.87+10:30
ISO_WEEK_DATE: 2014-W44-6+10:30
ISO_ZONED_DATE_TIME: 2014-11-01T22:13:48.87+10:30[Australia/Adelaide]
RFC_1123_DATE_TIME: Sat, 1 Nov 2014 22:13:48 +1030

So you see that these predefined formatters follow the accepted ISO and RFC specifications and won’t actually show the localised date format the way dates are formatted in e.g. Japan or the US.

We’ll take a look at date localisation using the DateTimeFormatter in the next post in this series.

View all posts related to Java here.

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

ARCHIVED: Bite-size insight on Cyber Security for the not too technical.