This article lists the main features added in Java 8 and their uses. like
- interfaces
- default methods
- static methods
- forEach() method in iterable interface (and Consumers)
- Lambda expressions
- double colon : Method reference operator
- functional interfaces
- Java Stream API
- Improvements in Collection API
- Java Time API
- Improvements in Concurrency API
- Java IO
- Core API
- withInitial(Supplier supplier) in ThreadLocal static method to create instances.
- default and static methods for Comparator interface
- natural ordering
- reverse order
- more…
- min(), max() and sum() methods in Integer, Long and Double wrapper classes.
- logicalAnd(), logicalOr() and logicalXor() methods in Boolean class.
- ZipFile.stream() method
- to get an ordered Stream over the ZIP file entries.
- Entries appear in the Stream in the order they appear in the central directory of the ZIP file.
- New utility methods in Math class.
- exact(), addExact(),substractExact(), incrementExact(), decrementExact(), multipyExact(), negateExact()
- floorDiv(), modeDiv(), nextDown()
jjs
command is added to invoke Nashorn Engine.jdeps
command is added to analyze class files- remove of JDBC-ODBC Bridge
- remove of PermGen memory space
- Nashorn
- Repeating annotations
- Statically-linked JNI libraries
- Built-in Functional Interfaces
- Predicates
- Functions
- Suppliers
- Consumers
- Comparators
- Optionals
Method reference operator (double colon :: ) in Java
Method reference operator is used to call a method by referring to it with the help of its class directly. The behaviour is the same as the lambda expressions. But the method reference operator uses a direct reference to the method by name instead of providing a delegate to the method.
Basically, Method References are compact, easy-to-read lambda expressions for methods that already have a name. They can be used in cases where lambda expression does nothing but call an existing method.
Use of method reference operator equivalent to a lambda expression
// using lambda expression stream.forEach( x-> System.out.println(x)); // using Method Reference Operator stream.forEach( System.out::println);
A Class Example could be
package com.techstartingpoint.examples.java8.methodReference; import java.util.List; import java.util.Arrays; public class MethodReferenceExampleReplaceLambda { public static void main(String[] args) { String[] anArray = {"one","two","three"}; // Get the stream List<String> myListOfStrings = Arrays.asList(anArray); // Print the list System.out.println(" Printing using lambda expression "); myListOfStrings.forEach(s -> System.out.println(s)); System.out.println(" Printing using Method Reference "); myListOfStrings.forEach(System.out::println); } }
About the parameters
Its formal parameter list must be the same used in the lambda function
e.g. :
x -> methodCalledInLambda(x) (x,y) -> methodCalledInLambda(x,y)
both can be replaced by
ClassContainingMethod::methodCalledInLambda
Example of Method Reference Operator with 2 parameters
package com.techstartingpoint.examples.java8.methodReference; import java.util.Arrays; import java.util.List; import java.util.function.BiPredicate; public class MethodReferenceWithTwoParameters { public static boolean isSumMultipleOf3(int i1, int i2) { return (i1+i2)%3 == 0; } public static void listElementsByCondition( List<Integer> initialList, BiPredicate<Integer, Integer> predicate) { for(Integer i : initialList) { if(predicate.test(i, i + 1)) { System.out.println(i+"+"+(i+1)+" = true"); } } } public static void main(String[] args) { List<Integer> numberList = Arrays.asList(1,2,3,4,5,6,7); System.out.println("using lambda expression with 2 parameters"); listElementsByCondition(numberList, (n1, n2) -> MethodReferenceWithTwoParameters.isSumMultipleOf3(n1, n2)); System.out.println("using method reference with 2 parameters"); listElementsByCondition(numberList,MethodReferenceWithTwoParameters::isSumMultipleOf3); } } }}
Static method
// static method (ClassName::staticMethodName) // instanceMethod (objectName::instanceMethodName) (new ClassName())::instanceMethodName // super method SuperClassName::superMethodName // Class constructor ClassName::new
Example of Method Reference Operator with Constructor
package com.techstartingpoint.examples.java8.methodReference; import java.util.Arrays; import java.util.List; public class MethodReferenceConstructorExample { public MethodReferenceConstructorExample(String aValue) { System.out.println("Creating an ExampleClass Instance :"+aValue); } public static void main(String[] args) { String[] anArray = {"one","two","three"}; // Get the stream List<String> myListOfStrings = Arrays.asList(anArray); // Print the list System.out.println(" Printing using lambda expression "); myListOfStrings.forEach(s -> new MethodReferenceConstructorExample(s)); System.out.println(" Printing using Method Reference "); myListOfStrings.forEach(MethodReferenceConstructorExample::new); } }
Example calling super method using Reference Method Operator
ParentClassExample.java
package com.techstartingpoint.examples.java8.methodReference.util; public class ParentClassExample { public String printAString(String str) { String returnValue="Parent Class print implementation " + str ; return returnValue; } }
ChildClassExample -> here is the super::method operator
package com.techstartingpoint.examples.java8.methodReference.util; import java.util.function.Function; public class ChildClassExample extends ParentClassExample { @Override public String printAString(String str) { String resultValueFromChildMethod="Child Class print implementation " + str ; System.out.println("child Value:"+resultValueFromChildMethod); System.out.println("calling to the parent class method"); Function<String,String> theParentMethod= super::printAString; String resultValueFromParentMethod = theParentMethod.apply(str); System.out.println("child Value:"+resultValueFromParentMethod); return resultValueFromChildMethod; } }
Class to execute the solution
package com.techstartingpoint.examples.java8.methodReference; import java.util.Arrays; import java.util.List; import com.techstartingpoint.examples.java8.methodReference.util.ChildClassExample; public class MethodReferenceSuperClassExample { public static void main(String[] args) { String[] anArray = {"one","two","three"}; // Get the stream List<String> myListOfStrings = Arrays.asList(anArray); // call the instance method // using double colon operator myListOfStrings.forEach((new ChildClassExample())::printAString); } }
Reference: https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html
Interfaces in Java 8
Default method
A default method on an interface that can be implemented.
Differences with abstract classes
- method of abstract classes can be private
- method of an abstract class can be inherited only by its subclasses
- default interface methods can be implemented only in the terms of calls to other interface methods
- default interface methods syntax allows extending the Collections Framework interfaces with lambda-oriented methods without breaking any existing implementations
- default interface methods allow providing a default implementation for defined contracts (Remember that an interface is a contract)
- A default method cannot override a method from java.lang.Object
What about implementing interfaces with default methods with the same signature?
In the case a class implements at least two interfaces which have a method with the same signature, the class must override (implement) that method. If the class does not implement that method it will be thrown a “Duplicated default method” at compilation time.
Example:
InterfaceA
package com.techstartingpoint.examples.java8.defaultMethod; @FunctionalInterface public interface InterfaceA { void method1(String str); // the method with repeated signature default void repeatedMethodName(String str){ System.out.println("I1 logging::"+str); } static void print(String str){ System.out.println("Printing "+str); } }
InterfaceB : Also with a declaration of repeatedMethodName
public interface InterfaceB { default void repeatedMethodName(String str){ System.out.println("Another repeated method:"+str); } }
The class implementing both Interfaces
package com.techstartingpoint.examples.java8.defaultMethod; public class ImplementationAandBClass implements InterfaceA,InterfaceB { @Override public void method1(String str) { // TODO Auto-generated method stub } }
This class throws a DuplicatedDefaultMethod error at compilation time
Static methods in Java 8 Interfaces
Static methods are implemented in interfaces. They have a body. The difference with interface default methods is that static methods can not be overridden. They cannot be implemented in classes.
interface static methods can only be called by methods declared in the same interface. They can be called by an interface default method, an interface static method or an interface method implemented in the class.
What about implementing interfaces which have static methods with the same signature.
There is no special consideration at all, as there is no ambiguity between them. The way to call a static interface method is
InterfaceName.staticMethodName(parameters);
then if there are two interfaces with a method with the same signature they will be called as
InterfaceName1.repeatedMethodName(parameter1); InterfaceName2.repeatedMethodName(parameter2);
Lambda Expressions in Java 8
Lambda expressions allow expressing instances of single-method classes more compactly.
The general syntax is
<parameter list> -> <body>
Where parameter list is a list of parameters enclosed in parenthesis. The list can be empty.
<body> it is a set of expressions and sentences enclosed in braces that define the behaviour of the lambda expression using the parameters.
Examples:
() -> {System.out.println("something")} (s) -> {System.out.println("I got the parameter "+s)} ((int) x, (int) y) -> {System.out.println(x+y)}
In lambda expressions, there are optional elements. Lambda expression can have
- optional type declaration in the parameters list
- optional parenthesis in parameters list (when there is only one parameter)
- optional braces in body section (when body contains only one statement)
- optional return keyword in body section (when it is implemented only by a “return” statement)
Check this meaningful piece of code to check lambda expressions syntax and behaviour
package com.techstartingpoint.examples.java8.lambda; public class LambdaExpressionExamples { // functional interfaces to be used as base types of resulting lambda // expressions public interface StringFunctionExample { String makeSomething(String firstString, String secondString); } public interface StringFunctionWithOneParameter { Integer makeSomethingWithOneParameter(String firstString); } // a functional interface to show Strings public interface PrintingService { void show(String s); } // a functional interface to show Integers public interface IntPrintingService { void show(Integer s); } // methods to run the resulting functional interfaces private static String runDoubleOperation(String s1, String s2, StringFunctionExample theFunction) { return theFunction.makeSomething(s1, s2); } private static Integer runSingleOperation(String s1, StringFunctionWithOneParameter theFunction) { return theFunction.makeSomethingWithOneParameter(s1); } public static void main(String args[]) { // with type declaration StringFunctionExample concatStrings = (String a, String b) -> a + b; // without type declaration StringFunctionExample getFirstString = (a, b) -> a; // with return keyword StringFunctionExample getFirstString2 = (a, b) -> { return a; }; // with several sentences in body StringFunctionExample sayHelloToBoth = (a, b) -> { String s1 = "Hello a"; String s2 = "Hello b"; String result = s1 + " and " + s2; return result; }; // one parameter with parenthesis - without return keyword StringFunctionWithOneParameter showOnlyTwo = (x) -> 2; // one parameter without parenthesis - without return keyword StringFunctionWithOneParameter getLength = x -> x.length(); // lambda expression to print results PrintingService thisSimplePrintingService = x -> System.out.println(x); IntPrintingService thisIntPrintingService = x -> System.out.println(x); thisSimplePrintingService.show(runDoubleOperation("Yellow", "World", concatStrings)); thisSimplePrintingService.show(runDoubleOperation("Only the First", "Only the second", getFirstString)); thisSimplePrintingService.show(runDoubleOperation("Only the First", "Only the second", getFirstString2)); thisSimplePrintingService.show(runDoubleOperation("Lisa", "Ann", sayHelloToBoth)); thisIntPrintingService.show(runSingleOperation("This string will not be shown", showOnlyTwo)); thisIntPrintingService.show(runSingleOperation("This string will not be shown but the length", getLength)); thisSimplePrintingService.show("Hello World"); } }
Lambda expressions can be used where Functional Interfaces are allowed. That’s why you should define a Functional Interface or use an existing Functional interface to “store” a lambda expression.
Lambda expression variable scope
Declarations in a lambda expression are interpreted just as they are in the enclosing environment.
package com.techstartingpoint.examples.java8.lambda; import java.util.function.Consumer; public class LambdaExpressionsScopeExample { public String s1 = "main value"; class InnerClassExample { public String s1 = "inner value"; // declaration of a parameter with the same name as the property (s1) void methodInInnerClass(String s1) { // The following statement causes the compiler to generate // the error "local variables referenced from a lambda expression // must be final or effectively final" in statement A: // // s1 = "another value"; Consumer<String> myConsumer = (receivedString) -> { // in lambda expressions body only can be referenced final variables or those that do not change System.out.println("s1: " + s1); // Statement A System.out.println("receivedString: " + receivedString); System.out.println("this.s1: " + this.s1); System.out.println("LambdaExpressionsScopeExample.this.s1: " + LambdaExpressionsScopeExample.this.s1); }; myConsumer.accept(s1); } } public static void main(String... args) { LambdaExpressionsScopeExample st = new LambdaExpressionsScopeExample(); LambdaExpressionsScopeExample.InnerClassExample fl = st.new InnerClassExample(); fl.methodInInnerClass("sample value"); } /* * Result: * * s1: sample value * receivedString: sample value * this.s1: inner value * LambdaExpressionsScopeExample.this.s1: main value */ }
Functional interfaces in Java 8
They are interfaces which can be used as the assignment target for a lambda expression or method reference.
The general interface body can contain
- abstract methods
- default methods
- static methods
This is also true for functional interfaces except that about abstract methods they can have only one. Any interface with a Single Abstract Method (SAM) is a functional interface, and its implementation may be treated as lambda expressions.
To be more precise, there is one kind of abstract method which does not count to this “only one limitation”. They are methods that have the same signature as the public methods of java.lang.Object class. A clear example is the Comparator interface. It has two abstract methods: compare and equals. But equals has the same signature of the equals method of java.lang.Object. Then it doesn’t count for the “only one abstract method restriction”. For that purpose “Comparator” has only one abstract method: compare.
Having only one abstract method, the basic implementation (not overriding any method) would be to implement the unique abstract method that exists. Then, can be implemented using a lambda expression.
Additionally, it is also recommended that functional interfaces have a @FunctionalInterface declaration
Example:
The Functional interface
package com.techstartingpoint.examples.java8.functionalInterface; @FunctionalInterface public interface OwnFunctionalInterface { // it is functional because it has only one abstract method (doSomething) public String doSomething(String base, int aNumber); default String duplicateDoSomething(String aBase) { return doSomething(aBase,8).concat(doSomething(aBase,10)); } static void doAnotherThing() { System.out.println("What do you expect?"); } }
The lambda expression that uses the functional interface
package com.techstartingpoint.examples.java8.functionalInterface; public class ClassUsingTheFunctionalInterface { public static void main(String args[]) { // lambda expression // lambda expression implementing my own Functional Interface // a is expected to be a String and b is expected to be an int as is declared in the unique asbtract method // of my OwnFunctionalInterface OwnFunctionalInterface ownInterfaceImplementedObject = (a, b) -> a.concat(":").concat(Integer.toString(b)); // then the created object implementing the interface can be used System.out.println("Result: "+ownInterfaceImplementedObject.duplicateDoSomething("other string --")); } }
The result of running the class is
Result: other string --:8other string --:10
Predefined functional interfaces
Java 8 provides a set of generic functional interfaces ready to be used. So, before to write down your own functional interface it’s a best practice to check if already exist one that fulfils the purpose you want in the default package.
https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html
To know how to use each of the pre-defined Functional Interfaces, it’s interesting to check what does the unique abstract method of each of them.
forEach() method in Iterable interface in Java
See – https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html
https://docs.oracle.com/javase/8/docs/api/java/util/function/Consumer.html
Iterable adds the forEach method that gets a “Consumer” argument
forEach(Consumer<? super T> action)
Iterable
until all elements have been processed or the action throws an exception.Consumer:
Consumer
is expected to operate via side-effects. Basically a Consumer is “something” that makes “something” on a value. What does it do? It does what is defined in the method accept.accept(Object)
.package com.techstartingpoint.examples.java8; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.function.Consumer; public class ForEachExample { public static void main(String[] args) { //creating sample Collection // Remember List implements Iterable List iterableList = new ArrayList(); for(int i=0; i<10; i++) iterableList.add("element:"+i); // Previous way : using Iterator // 1st example System.out.println("1st Example using iterator"); Iterator it = iterableList.iterator(); while(it.hasNext()){ String i = it.next(); System.out.println("Element Value::"+i); } System.out.println(); // Iterable.forEach method with anonymous class // Using "String" because is the type used in our list // 2nd example System.out.println("2nd Example using forEach with anonymous Consumer class"); iterableList.forEach(new Consumer() { public void accept(String element) { System.out.println("forEach anonymous element value::"+element); } }); System.out.println(); // Implement declaring our own class implementing Consumer // 3rd Example System.out.println("3rd Example using forEach with a Consumer implementation"); ConsumerImplementation action = new ConsumerImplementation(); iterableList.forEach(action); System.out.println(); } } // Consumer implementation // supporting 3rd example class ConsumerImplementation implements Consumer{ public void accept(String element) { System.out.println("forEach element value::"+element); } }
Java Stream API
Java generally provides collections to store and manipulate sets of data. The data in collections can be organized and retrieved in different ways. That’s possible because Java provides many classes and interfaces related to collections.
Streams provide another way to access data sources like Collections. Streams allow us to make bulk processing convenient and fast. Streams do not provide elements to store data but to read it and process it, but not changing the original source.
Java Stream pipelines
Streams allow organizing work in a pipeline way. Methods related to streams generally return a stream. Then we can apply another stream method to a result of a stream method. Operations provided by streams can be easily chained generating a pipeline.
A stream pipeline consists of
- a source (which might be an array, a collection, a generator function, an I/O channel, etc)
- zero or more intermediate operations (which transform a stream into another stream)
- a terminal operation (which produces a result or side-effect, such as count() or forEach(Consumer)).
Each intermediate operation in the stream pipeline is lazily executed and returns a stream as a result. The last method call on the pipeline marks the end of the stream and returns the final result.
Java Streams (Aggregate operations) vs External Iterators
Despite operations made using streams can be done using iterators there are several differences between them.
Streams use internal Iterations
Stream operations do not contain a method like next. The JDK determines how to iterate the collection.
Using iterations can only iterate over the elements of a collection sequentially. Streams operations can more easily take advantage of parallel computing.
Streams do not modify the source Collection
Streams support functionality as parameters via lambda expressions.
Possibly unbounded
The elements of a stream do not need to be finite.
Stream Creation
https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html
There are several ways to create a Stream. As stream operations results are streams then the operations allow creating new streams.
Some examples of creating streams from no stream sources are:
- Static Factory methods of Stream class
- Stream.of(…)
- Stream.iterate(…)
- Stream.generate(…)
- Stream.empty()
- stream methods of Collections and Arrays
- Arrays.stream()
- aListObject.stream()
- aListObject.parallelStream()
- from Iterator via Spliterator
See the Java Code Stream Examples
Stream operations
The full list of stream operations can be found at https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
Some of them are terminal operations (which do not produce a stream).
- Terminal operations
- forEach
- forEachOrdered: processes the items in the stream in the order they appear. It does not process them in the order established by comparable
- count
- toArray
- reduce: summarizes elements of streams based on a Collector Method or custom defined behavior
- collect
- Collectors Methods
- toList
- joining
- toSet
- toCollection
- summarizingDouble
- partitioningBy
- groupingBy
- mapping
- reducing
- Collectors Methods
- returning Optional
- min
- max
- findFirst
- findAny
- Returning boolean
- allMatch
- anyMatch
- noneMatch
- Intermediate Operations (returning Stream)
- map
- filter
- flatMap
- peek
- sorted
- distinct
- Specialized Operations
- sum
- average
- range
- summaryStatistics
- Parallel Streams
- parallel
- Infinite Streams
- generate
- iterate
There are some similar functionality but conceptually different. For example stream.map(). is a intermediate operation but collect(Collectors.mapping) is a terminal opration. The same happens between stream.reduce() and collect(Collectors.reducing)
Source code example of several stream operations
package com.techstartingpoint.examples.java8.streams; import java.util.Arrays; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; public class StreamOperationExamples { static Product[] arrayOfProducts = { new Product(4, 12.50,"Marking Knife", "Cutters & Stencils",new String[] { "part 1", "part 2", "part3" , "part4" }) , new Product(1, 12.50,"Gauge", "John & John",new String[] { "part A", "part B", }), new Product(2, 10.20,"Hammer", "John & John",new String[] { "part A1", "part B1", }), new Product(3, 3.40,"Rule", "Measures Inc.",new String[] { "part A", "part B2", }) }; static Product[][] arrayOfArraysOfProducts = {arrayOfProducts,arrayOfProducts,arrayOfProducts}; private static class Product implements Comparable<Product> { int id; double price; String name; String brand; String[] parts; public Product(int id, double price, String name, String brand,String[] parts) { super(); this.id = id; this.price = price; this.name = name; this.brand = brand; this.parts = parts; } @Override public int compareTo(Product arg0) { return this.name.compareTo(arg0.name); } @Override public String toString() { return "Product [id=" + id + ", name=" + name + "]"; } } public static void main(String[] args) { System.out.println("Products ----- forEach"); // use of forEach Stream<Product> productsStream = Stream.of(arrayOfProducts); productsStream.forEach(x -> System.out.println(x.name)); System.out.println(); System.out.println("Products ----- forEachOrdered"); // use of forEach // if stream is not created again it throws "stream has already been operated upon or closed" productsStream = Stream.of(arrayOfProducts); // forEachOrdered process the items in the stream in the order they appear // it does not process them in a order established by comparable productsStream.forEachOrdered(x -> System.out.println(x.name)); System.out.println(); System.out.println("products count"); System.out.println(Stream.of(arrayOfProducts).count()); System.out.println(); System.out.println("products map and reduce example - Generate product names joined by ;"); System.out.println(Stream.of(arrayOfProducts).map(p1 -> p1.name).reduce((name1,name2) -> name1+ "-" +name2)); System.out.println(); System.out.println("Reducer with 3 arguments(Identity, Accumulator, Combiner) -> Combiner only works with paralell stream"); System.out.println(Stream.of(arrayOfProducts).parallel().reduce("", (summaryString,product) -> summaryString+ " - " +product.name, (x,y) -> x + "//" + y)); System.out.println(); System.out.println("Stream map and collect example using Collectors.joining with ,"); System.out.println(Stream.of(arrayOfProducts).map(p1 -> p1.name).collect(Collectors.joining(","))); Stream fromMap = Stream.of(arrayOfArraysOfProducts).map(x -> Arrays.stream(x)); Stream fromFlatMap = Stream.of(arrayOfArraysOfProducts).flatMap(x -> Arrays.stream(x)); System.out.println("from map -----------------------------------"); fromMap.forEach(x -> System.out.println(x.getClass())); System.out.println("from flatMap -----------------------------------"); fromFlatMap.forEach(x -> System.out.println(x.getClass())); // in this case each element is mapped to a stream fromMap = Stream.of(arrayOfProducts).map(x -> Arrays.stream(x.parts)); // in this case each element is mapped to n elements present in the generated stream fromFlatMap = Stream.of(arrayOfProducts).flatMap(x -> Arrays.stream(x.parts)); System.out.println("from map -> parts-----------------------------------"); fromMap.forEach(System.out::println); System.out.println("from flatMap -> parts -----------------------------------"); fromFlatMap.forEach(System.out::println); System.out.println(); System.out.println("Partitioning by brand = John & John"); System.out.println(Stream.of(arrayOfProducts).collect(Collectors.partitioningBy(x -> x.brand.contentEquals("John & John")))); System.out.println(); System.out.println("Grouping by brand"); System.out.println(Stream.of(arrayOfProducts).collect(Collectors.groupingBy(x -> x.brand,Collectors.counting()))); System.out.println(); System.out.println("Filter byby brand = John & John"); System.out.println(Stream.of(arrayOfProducts).filter(x -> x.brand.contentEquals("John & John"))); } }
Date Time API in Java 8
Why improvement was needed on Date Time API in Java?
Drawbacks of the previous version
- Not consistent definition
- Previous
- Different behaviour between java.util and java.sql
- Java 8
- java.time is the base package
- LocalDate
- LocalTime
- LocalDateTime
- Instant: An instantaneous point on the time-line; it stores date time in unix timestamp
- Period
- Duration
- java.time is the base package
- Lack of formatting and parsing unified classes (java.util.text)
- Previous
- Different meaning
- java.uitl.Date -> Date and Time
- java.sql.Date -> only Date
- Same name for both
- Formating and Parsing
- Previous:
- Confusing definitions of Time / Timestamp / Formating / Parsing
- DateFormat / SimpleDateFormat
- Java 8
- All of them are in the java.time.format package
- Main classes provide formatting and parsing methods
- Previous:
- Concurrency
- Previous
- No thread Safe (all classes are mutable)
- Java 8
- Most classes are immutable and thread-safe
- Previous
- Lack of essential features
- Previous
- no i18n
- no TimeZone support
- java.util.Calendar and java.util.Timezone is also no thread-safe
- Java 8
- All of them are in java.time.zone package
- Previous
- Additional Features
- java.time.chrono: Generic API for calendar systems other than the default ISO.
- java.time.temporal: Access to date and time using fields and units, and date time adjusters.
Previous to Java 8 Joda-time arose to solve several of those problems. Most of them are solved by Java 8 now.
Improvements in Collections API
Iterator.forEachReamaining(…)
It is equivalent to forEach but performs the given action for each remaining element until all elements have been processed or the action throws an exception.
It’s equivalent to
while (hasNext())
action.accept(next());
using the forEach call syntax.
Like in previous versions, iterators allow removing elements while iterating.
Collection.removeIf(Predicate…)
Removes all of the elements of this collection that satisfy the given predicate.
Example
collection.removeIf( x->(x<=123));
Spliterator
An object for traversing and partitioning elements of a source
Differences between Iterator and Spliterator
- Used with:
- Spliterator
- Streams in Java 8.
- Parallel
- Sequential
- Internal Iteration
- Streams in Java 8.
- Iterator
- Collection.
- Only Sequential
- External Iteration
- Collection.
- Spliterator
- Traversing
- Spliterator
- Individually
- Bulk
- Iterator
- Individually
- Spliterator
Main methods
tryAdvance
boolean tryAdvance(Consumer<? super T> action)
If a remaining element exists, performs the given action on it, returning true
; else returns false
.
Example
while
(spliterator.tryAdvance(x -> x.setAddress("A fixed Value or a calculation"
)) {
operations++;
}
trySplit
it tries to split the splititerator in two parts if it can be performed. Near half of the elements are in the result after successful execution and the other half in the initial Splititerator
The causes that make not possible to split a stream could be:
- empty stream
- inability to split after traversal has commenced
- data structure constraints
- efficiency considerations
Source code example
package com.techstartingpoint.examples.java8.SpliteratorExample; import java.util.Arrays; import java.util.List; import java.util.Spliterator; public class TrySplitExample { public static void main (String[] args) { List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8); Spliterator<Integer> initialSpliterator = list.spliterator(); Spliterator<Integer> generatedAfterFirstSplit = initialSpliterator.trySplit(); System.out.println("Traversing: initial"); initialSpliterator.forEachRemaining(System.out::println); System.out.println("Traversing: generated by trySplit"); generatedAfterFirstSplit.forEachRemaining(System.out::println); } }
Using Spliterator elements can be partitioned and iterated in parallel. Spliterator itself does not provide parallel processing behaviour. Instead, the Spliterator provides an easy way to partition the stream in 2 parts that could be processed independently.
Map Methods
replaceAll
Syntax
default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function)
Replaces each entry’s value with the result of invoking the given function on that entry until all entries have been processed or the function throws an exception.
The functionality is equivalent to
for (Map.Entry<K, V> entry : map.entrySet())
entry.setValue(function.apply(entry.getKey(), entry.getValue()));
Source Code Example
myMap
.replaceAll((key, oldValue)
-> oldValue + 2);
compute
It replaces the value only for the specified key
Syntax
default V compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)
Source Code Example
myMap.compute(key, (k, v) -> "New Value");
merge
If the specified key is not already associated with a value or is associated with null, associates it with the given non-null value. Otherwise, replaces the associated value with the results of the given remapping function, or removes if the result is null
. This method may be of use when combining multiple mapped values for a key. For example, to either create or append a String msg
to a value mapping:
Syntax
default V merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction)
Source Code Example
myMap.merge(
key,
value,
(s1, s2)
-
> s1.equals(s2)
? s1.toUpperCase()
: s1 +
", "
+ s2)
Improvements in Concurrency APIs