Java 8 Features

  1. Lambda Expression
  2. Method References
  3. Functional Interfaces
  4. Interface default and static methods
  5. Streams
  6. Foreach
  7. Optional
  8. StringJoiner
  9. Collectors class
  10. Array Parallel Sort
  11. Date Time API
  12. CompletableFuture



Subscribe To Our Newsletter
You will receive our latest post and tutorial.
Thank you for subscribing!

required
required


CompletableFuture

 

CompletableFuture is used for asynchronous or non-blocking programming in Java. With asynchronous programming, you can have one or many tasks being processed at the same time. This is done by using separate threads, other than the main thread, to process other tasks. The main thread is not blocked or waiting so tasks can be done in parallel. The main thread is just notified when other threads are done with their tasks whether it’s completion or failure.

A Future is used as a reference to the result of an asynchronous computation. It provides an isDone() method to check whether the computation is done or not, and a get() method to retrieve the result of the computation when it is done.

 

CompletableFuture<String> realName = restService.getName();
		
try {
	log.info("sleeping 1000 millseconds");
		
	TimeUnit.SECONDS.sleep(5);
	if (realName.isDone()) {
		log.info("yeah here is your realname={}", realName.get());
	}else {
		log.info("nah realname has not found yet");
	}
} catch (InterruptedException | ExecutionException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
}

 

 

 

		CompletableFuture<ArrayNode> plUsers = restService.getPlaceHolderTestingList(RestService.PLACEHOLDER_USER);
		
		CompletableFuture<ArrayNode> plTodos = restService.getPlaceHolderTestingList(RestService.PLACEHOLDER_TODO);
		
		CompletableFuture<ArrayNode> plComments = restService.getPlaceHolderTestingList(RestService.PLACEHOLDER_COMMENT);
		
		CompletableFuture<ArrayNode> plPhotos = restService.getPlaceHolderTestingList(RestService.PLACEHOLDER_PHOTO);
		
		CompletableFuture.allOf(plUsers,plTodos,plComments,plPhotos).join();
		
		try {
			log.info("plUsers={}",ObjectUtils.toJson(plUsers.get()));
			log.info("plTodos={}",ObjectUtils.toJson(plTodos.get()));
			log.info("plComments={}",ObjectUtils.toJson(plComments.get()));
			log.info("plPhotos={}",ObjectUtils.toJson(plPhotos.get()));
			
		} catch (InterruptedException | ExecutionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

 

 

		List<String> placeHolders = Arrays.asList(RestService.PLACEHOLDER_USER, RestService.PLACEHOLDER_TODO);

		CompletableFuture<ArrayNode>[] futures = new CompletableFuture[placeHolders.size()];

		for (int i = 0; i < futures.length; i++) {
			CompletableFuture<ArrayNode> pl = restService.getPlaceHolderTestingList(placeHolders.get(i));
			futures[i] = pl;
		}

		CompletableFuture.allOf(futures).join();

		try {
			for (int i = 0; i < futures.length; i++) {
				ArrayNode plhds = futures[0].get();
				log.info("plhds={}", ObjectUtils.toJson(plhds));
			}

		} catch (InterruptedException | ExecutionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

 

September 24, 2019

Array Parallel Sort

 

The sorting algorithm is a parallel sort-merge that breaks the array into sub-arrays that are themselves sorted and then merged. When the sub-array length reaches a minimum granularity, the sub-array is sorted using the appropriate Arrays.sort method. If the length of the specified array is less than the minimum granularity, then it is sorted using the appropriate Arrays.sort method. The algorithm requires a working space no greater than the size of the original array. The ForkJoin common pool is used to execute any parallel tasks.

public class ParallelArraySort {
 
    public static void main(String[] args) {
        
        int[] intArray = {73, 1, 14, 2, 15, 12, 7, 45};
        
        System.out.println("---Before Parallel Sort---");
        for(int i : intArray)
            System.out.print(i+" ");
        
        //Parallel Sorting
        Arrays.parallelSort(intArray);
        
        System.out.println("\n---After Parallel Sort---");
        for(int i : intArray)
            System.out.print(i+" ");
    }
}

Result:

---Before Parallel Sort---
73 1 14 2 15 12 7 45 
---After Parallel Sort---
1 2 7 12 14 15 45 73 

Let’s check how efficient parallel sort compared to sequential sort.

private static void timeSort() {
		int[] arraySizes = { 10000, 100000, 1000000, 10000000 };

		for (int arraySize : arraySizes) {

			System.out.println("When Array size = " + arraySize);

			int[] intArray = new int[arraySize];
			Random random = new Random();

			for (int i = 0; i < arraySize; i++)
				intArray[i] = random.nextInt(arraySize) + random.nextInt(arraySize);

			int[] forSequential = Arrays.copyOf(intArray, intArray.length);
			int[] forParallel = Arrays.copyOf(intArray, intArray.length);

			long startTime = System.currentTimeMillis();
			Arrays.sort(forSequential);
			long endTime = System.currentTimeMillis();

			System.out.println("Sequential Sort Milli seconds: " + (endTime - startTime));

			startTime = System.currentTimeMillis();
			Arrays.parallelSort(forParallel);
			endTime = System.currentTimeMillis();

			System.out.println("Parallel Sort Milli seconds: " + (endTime - startTime));
			System.out.println("------------------------------");

		}
}

Result:

When Array size = 10000
Sequential Sort Milli seconds: 3
Parallel Sort Milli seconds: 6
------------------------------
When Array size = 100000
Sequential Sort Milli seconds: 11
Parallel Sort Milli seconds: 27
------------------------------
When Array size = 1000000
Sequential Sort Milli seconds: 148
Parallel Sort Milli seconds: 28
------------------------------
When Array size = 10000000
Sequential Sort Milli seconds: 873
Parallel Sort Milli seconds: 215
------------------------------

As you can see, when the array size is small sequential sorting performs better but when the array size is large parallel sorting performs better. So figure out which sorting algorithm to use you must know the size of the array and determine whether such size is small or large for your use case.

Also, you can sort an array partially meaning you can sort an array from a certain position to another certain position.

private static void sortPartial() {
		int[] intArray = { 18, 1, 14, 2, 15, 12, 5, 4 };

		System.out.println("Array Length " + intArray.length);
		System.out.println("---Before Parallel Sort---");

		for (int i : intArray)
			System.out.print(i + " ");

		// Parallel Sorting
		Arrays.parallelSort(intArray, 0, 4);

		System.out.println("\n---After Parallel Sort---");
		for (int i : intArray)
			System.out.print(i + " ");
}

Result:

Array Length 8
---Before Parallel Sort---
18 1 14 2 15 12 5 4 
---After Parallel Sort---
1 2 14 18 15 12 5 4 

 

August 5, 2019

Date Time API

 

Issues with Old Date API

  • Thread Safety – The Date and Calendar classes are not thread safe, leaving developers to deal with the headache of hard to debug concurrency issues and to write additional code to handle thread safety. On the contrary the new Date and Time APIs introduced in Java 8 are immutable and thread safe, thus taking that concurrency headache away from developers.
  • APIs Design and Ease of Understanding – The Date and Calendar APIs are poorly designed with inadequate methods to perform day-to-day operations. The new Date/Time APIs is ISO centric and follows consistent domain models for date, time, duration and periods. There are a wide variety of utility methods that support the commonest operations.
  • ZonedDate and Time – Developers had to write additional logic to handle timezone logic with the old APIs, whereas with the new APIs, handling of timezone can be done withLocal and ZonedDate/Time APIs.

Date and Time official documentation.

LocalDate is an immutable date-time object that represents a date, often viewed as year-month-day. Other date fields, such as day-of-year, day-of-week and week-of-year, can also be accessed. For example, the value “2nd October 2007” can be stored in a LocalDate. This class does not store or represent a time or time-zone.

How to create a LocalDate

// use now() method
LocalDate now = LocalDate.now(ZoneId.systemDefault());//2019-09-10
// use of() method
LocalDate date1 = LocalDate.of(2019, 6, 20);//2019-06-20
// use parse() method
LocalDate date2 = LocalDate.parse("2016-08-16").plusDays(2);//2016-08-18

How to manipulate LocalDate

// add days
LocalDate date2 = LocalDate.now().plusDays(2);//2016-08-18
// minus
date2 = date2.minus(1, ChronoUnit.MONTHS);//2016-07-18
// plus
//date2 = date2.plus(3, ChronoUnit.MONTHS);//2016-10-18

 

LocalDateTime is an immutable date-time object that represents a date-time, often viewed as year-month-day-hour-minute-second. Other date and time fields, such as day-of-year, day-of-week, and week-of-year, can also be accessed. Time is represented to nanosecond precision. For example, the value “2nd October 2007 at 13:45.30.123456789” can be stored in a LocalDateTime. This class does not store or represent a time-zone.

How to create a LocalDateTime

// use now() method
LocalDateTime now = LocalDateTime.now(ZoneId.systemDefault());
// use of() method
 LocalDateTime date1 = LocalDateTime.of(2019, 6, 20, 0, 0).plusDays(2);// 2019-06-22T00:00

How to manipulate LocalDateTime

// add days
LocalDateTime date1 = LocalDateTime.of(2019, 6, 20, 0, 0).plusDays(2);//2019-06-22T00:00
// minus
date1 = date1.minus(1, ChronoUnit.MONTHS);//2019-05-22T00:00
// plus
date1 = date1.plus(3, ChronoUnit.MONTHS);//2019-08-22T00:00

LocalTime is an immutable date-time object that represents a time, often viewed as hour-minute-second. Time is represented to nanosecond precision. For example, the value “13:45.30.123456789” can be stored in a LocalTime.

LocalTime now = LocalTime.now(Clock.systemDefaultZone());
System.out.println("now = " + now);
    	
LocalTime time1 = LocalTime.of(12, 6).plusHours(2);
System.out.println("time1 = " + time1);
    	
time1 = time1.minus(1, ChronoUnit.MINUTES);
System.out.println("time1 = " + time1);
    	
time1 = time1.plus(3, ChronoUnit.HOURS);
System.out.println("time1 = " + time1);

 

August 5, 2019

Optional

 

Optional is a container object that may or may not contain a value. You can check if a value is present by calling the isPresent() method. If isPresent() returns true you can use the get() method to return the value.

Null pointer exception is a real problem in programming and that we must handle it with care.

How to create an Optional

String name = "Folau";
// name can't be null or else you will get a NullPointerException
Optional<String> opt = Optional.of(name);

If we expect null values, we can use the ofNullable method which does not throw a NullPointerException

String name = "Folau";
Optional<String> opt = Optional.ofNullable(name);

String name = null;
// this won't throw a NullPointerException
Optional<String> opt = Optional.ofNullable(name);

java8-optional-f2

Check if Optional is empty or not

The Optional.isPresent() method is used to check if an Optional has a value or not. It returns true if the Optional has value otherwise it returns false.

String name = "Folau";
Optional<String> opt = Optional.ofNullable(name);

if (opt.isPresent()) {
	System.out.println("Value available.");
} else {
	System.out.println("Value not available.");
}

The Optional.ifPresent() method is used to execute a lambda function (on the value) if Optional is not empty. if Optional is empty, do nothing.

opt.ifPresent(n -> {
	System.out.println(n);
});

Optional.orElse(T other) returns the value if present, otherwise return other of same data type
Optional.orElseGet(Supplier T) returns the value if present, otherwise invoke T and return the result of that call.

//when using orElse(), whether the wrapped value is present or not, the default object is created. 
//So in this case, we have just created one redundant object that is never used.
String n = opt.orElse(getRealName());
		
System.out.println(n);
		
String na = opt.orElseGet(() -> getRealName());
		
System.out.println(na);

....

private String getRealName() {
	System.out.println("getRealName()");
	return "Lisa";
}

// Run program result
getRealName()
Folau
Folau

Optional.filter(Predicate T) – If a value is present, and the value matches the given predicate, return an Optional describing the value, otherwise return an empty Optional.

Optional<Integer> ageOpt = Optional.ofNullable(20);

boolean underAgeForBeer = ageOpt.filter(age -> {
	return (age >= 21);
}).isPresent();

System.out.println("underAgeForBeer = " + underAgeForBeer);

Optional.map(Function mapper) – If a value is present, apply the provided mapping function to it, and if the result is non-null, return an Optional describing the result. Otherwise return an empty Optional.

Optional<String> optFileName = Optional.ofNullable("file.pdf");
		
Optional<File> optFile = optFileName.map(fileName -> new File(fileName));

 

 

August 5, 2019

StringJoiner

 

SStringJoiner is used to construct a sequence of characters separated by a delimiter and optionally starting with a supplied prefix and ending with a supplied suffix.

Prior to adding something to the StringJoiner, its sj.toString() method will, by default, return prefix + suffix. However, if the setEmptyValue method is called, the emptyValue supplied will be returned instead. This can be used, for example, when creating a string using set notation to indicate an empty set, i.e. "{}", where the prefix is "{", the suffix is "}" and nothing has been added to the StringJoiner.

import java.util.StringJoiner;

public class StringJoinerDemo {

	public static void main(String[] args) {
		String delimeter = "-";
		StringJoiner names = new StringJoiner(delimeter);
		
		names.add("Laulau");
		names.add("Kinga");
		names.add("Fusi");
		
		System.out.println(names);
	}
}

Result:

Laulau-Kinga-Fusi

 

Using prefix and suffix

public static void demoWithPrefixAndSuffix() {
		String delimeter = ",";
		String prefix = "(";
		String suffix = ")";
		StringJoiner names = new StringJoiner(delimeter, prefix, suffix);
		
		names.add("Laulau");
		names.add("Kinga");
		names.add("Fusi");
		
		System.out.println(names);
}

Result:

(Laulau,Kinga,Fusi)

Using merge

public static void demoWithMerge() {
		String delimeter = ",";
		String prefix = "(";
		String suffix = ")";
		StringJoiner names1 = new StringJoiner(delimeter, prefix, suffix);

		names1.add("Laulau");
		names1.add("Kinga");
		names1.add("Fusi");

		System.out.println(names1);

		StringJoiner names2 = new StringJoiner(delimeter, prefix, suffix);

		names2.add("John");
		names2.add("Peter");
		names2.add("Andrew");

		System.out.println(names2);
		
		StringJoiner names3 = names1.merge(names2);
		
		System.out.println(names3);
}

Result:

(Laulau,Kinga,Fusi)
(John,Peter,Andrew)
(Laulau,Kinga,Fusi,John,Peter,Andrew)

 

 

 

 

 

 

 

August 5, 2019