Introduction
Prior to JDK 8, Java Interfaces can contain only abstract
methods and constants. If you wish to add an additional abstract
method to an existing interface, you need to re-write all the existing classes that implement this interface to support this additional method. This makes enhancement to existing interfaces (including those in the Collection Framework such as Collection
and List
) difficult.
JDK 8 introduces public default
and public static
methods into Interface. JDK 9 further introduces private
and private static
method. (See "Interface public default, public static, private and private static methods"). Adding these non-abstract
methods to existing interfaces does not require re-writing the existing implementation classes. As a result, JDK 8 is able to enhance many interfaces in the Collection framework.
JDK 8 also introduces Lambda Expressions as a shorthand for creating an instance that implements a single-abstract-method interface (or functional interface) via anonymous inner class (See "Lambda Expressions"), new Function API and new Stream API (See "Stream API") to support Functional Programming (See "Functional Programming in Java"). The retrofitted Collection Framework in JDK 8 makes extensive use of these new features.
If you are not familiar with the new features in JDK 8, I suggest you read through the article "Lambda Expression, Streams and Functional Programming".
Collection API Enhancements since JDK 8
The Collection framework is defined via many interfaces, such as java.util.Collection<E>
, java.util.List<T>
, java.util.Set<T>
, and java.util.Map<K,V>
. With the introduction of default
and static
methods in interfaces, JDK 8 enhances these interfaces by adding many default
and static
methods, without breaking any existing implementation classes.
You can check out these new methods from the JDK API documentation. Select the desired interface ⇒ "SUMMARY METHOD" ⇒ "Default Methods" or "Static Methods".
Interface Iterable<T> New default Method .forEach() (JDK 8)
JDK 8 added a new default
instance method .forEach()
to the java.lang.Iterable<T>
interface to consume each of the elements of the collection. Since java.util.Collection<T>
is a subtype of Iterable<T>
, .forEach()
is available to all Collection
objects.
The definition of .forEach()
is:
// Interface java.lang.Iterable<T> default void forEach(java.util.function.Consumer<? super T> action) { Objects.requireNonNull(action); // Ensure that action is NOT null for (T t : this) { action.accept(t); } }
The Consumer<T>
is a functional interface (single-abstract-method interface) defined as follow:
// Functional Interface java.util.function.Consumer<T> @FunctionalInterface public interface Consumer<T> { void accept(T t); // public abstract ...... }
In Consumer<T>
, the single abstract method (called accept()
) takes an object of type T
, performs its programmed operations, and returns void
. In JDK 8, you can create an instance of Functional Interface via the shorthand Lambda Expressions.
In .forEach()
, this Consumer
will be applied to each of the elements of the Collection
.
Example
Person.java:
public class Person { private String name; // private instance variables private int age; public Person(String name, int age) { this.name = name; this.age = age; } // constructor public String getName() { return name; } // getter public int getAge() { return age; } public String toString() { return name + "(" + age + ")"; } // toString() to describe itself // To be used in testing Consumer public void sayHello() { System.out.println(name + " says hello"); } }
J8IterableForEachTest.java:
import java.util.List; import java.util.Iterator; public class J8IterableForEachTest { public static void main(String[] args) { List<Person> pList = List.of( // JDK 9 to construct an unmodifiable List new Person("Paul", 60), new Person("John", 15)); System.out.println(pList); //[Paul(60), John(15)] // Pre-JDK 8: Using a for-each loop for (Person p: pList) { System.out.println(p); } //Paul(60) //John(15) // Pre-JDK 8: Using an Iterator Iterator<Person> iter = pList.iterator(); while (iter.hasNext()) { Person p = iter.next(); System.out.println(p.getName()); } //Paul //John // JDK 8 introduces .forEach() in Iterable<T> pList.forEach(p -> System.out.println(p)); // Use Lambda Expression //Paul(60) //John(15) pList.forEach(System.out::println); // Use method reference (same as above) pList.forEach(Person::sayHello); // Use method reference to invoke an instance method //Paul says hello //John says hello pList.forEach(p -> System.out.print(p.getName() + " ")); // Another Lambda Expression //Paul John // JDK 8 also introduces Stream API, with a forEach() terminal operation, // which is different from Iterable's forEach(). See "Stream API" pList.stream().filter(p -> p.getAge() >= 21).forEach(System.out::println); //Paul(60) } }
[TODO] Explanation
Notes: The Iterable
's .forEach()
is different from Stream
's .forEach()
introduced in JDK 8 Stream API.
New Class java.util.Optional<T> (JDK 8)
JDK 8 introduces a new container class java.util.Optional<T>
as a container which may contain a value or nothing. It is primarily intended for use as a method return-type, where the method may return a non-null value or nothing. Instead of returning null
, which may trigger NullPointerException
, you can return an Optional.empty()
.
The commonly-used methods in java.util.Optional<T>
are:
public final class Optional<T> { static <T> Optional<T> empty() // static factory method to return an empty Optional instance static <T> Optional<T> of(T value) // static factory method to return a non-null value instance boolean isEmpty() boolean isPresent() T get() T orElse(T other) // To support Functional Programming Optional<T> filter<Predicate<? super T> p) <U> Optional<U> map(Function<? super T, ? extends U> mapper) void ifPresent(Consumer<? super T> action) void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) Stream<T> stream() ...... }
For example,
import java.util.Optional; public class JDK8OptionalTest { public static void main(String[] args) { Optional<String> o1 = Optional.of("apple"); System.out.println(o1); //Optional[apple] System.out.println(o1.isPresent()); //true System.out.println(o1.get()); //apple System.out.println(o1.orElse("orange")); //apple o1.ifPresent(System.out::println); //apple System.out.println(o1.filter(v -> v.charAt(0) == 'a')); //Optional[apple] System.out.println(o1.filter(v -> v.charAt(0) == 'o')); //Optional.empty System.out.println(o1.map(v -> v.charAt(0))); //Optional[a] // empty optional instance Optional<String> o2 = Optional.empty(); System.out.println(o2); //Optional.empty System.out.println(o2.isEmpty()); //true //System.out.println(o2.get()); //java.util.NoSuchElementException: No value present System.out.println(o2.orElse("orange")); //orange o2.ifPresent(System.out::println); //no output } }
Primitive type specializations of OptionalInt
, OptionalDouble
, OptionalLong
are also available.
Instantiation of an Unmodifiable Collection: New static Factory Method .of() and .copyOf()
JDK 9/10 introduces new static
factory method .of()
and .copyOf()
in List
, Set
, Map
, Optional
, etc. to generate an instance of unmodifiable collection. In an unmodifiable collections, elements cannot be added, removed or replaced. Calling any mutator will trigger an UnsupportedOperationException
.
List.of() and Set.of() (JDK 9)
JDK 9 introduces 11 new overloaded static
factory methods List.of()
and Set.of()
to generate an unmodifiable List/Set
, with 0 to 10 elements. The signatures are as follows:
// Interface java.util.List<E> static <E> List<E> of() // 11 overloaded methods to support 0 to 10 elements static <E> List<E> of(e1) static <E> List<E> of(e1, e2) ...... static <E> List<E> of(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10) // Interface java.util.Set<E> static <E> Set<E> of() // 11 overloaded methods to support 0 to 10 elements static <E> Set<E> of(E e1) static <E> Set<E> of(E e1, E e2) ...... static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10)
For example,
import java.util.List; import java.util.Set; public class J9ListSetOfTest { // JDK 9 public static void main(String[] args) { List<String> lst = List.of("apple", "orange"); // 0 to 10 elements only System.out.println(lst); //[apple, orange] System.out.println(lst.get(0)); //apple (List is ordered with numerical index) // Unmodifiable //lst.add("banana"); //java.lang.UnsupportedOperationException Set<Double> set = Set.of(1.1, 2.2, 3.3); // 0 to 10 elements only System.out.println(set); //[3.3, 1.1, 2.2] (Set is unordered) // Unmodifiable //set.clear(); //java.lang.UnsupportedOperationException } }Notes:
- You will get an
UnsupportedOperationException
if you try to modify an unmodifiableCollection
object. - The underlying implementation class is not known, but non-modifiable.
- There are 11 overloaded
static
factory methods for handling 0 to 10 elements. Check out the API documentation.
Map.of(), Map.ofEntries() and Map.entry() (JDK 9)
JDK 9 introduces 11 new overloaded static
factory methods Map.of()
to generate an unmodifiable Map
, with 0 to 10 key-value pairs. It also introduces new static
methods Map.ofEntries()
and Map.entry()
to generate an unmodifiable Map
, with arbitrary number of key-value pairs.
The signatures is as follows:
// Interface java.util.Map<K,V> static <K,V> Map<K,V> of() // 11 overloaded methods to support 0 to 10 key-value pairs static <K,V> Map<K,V> of(K k1, V v1) static <K,V> Map<K,V> of(K k1, V v1, K k2, V v2) ...... static <K,V> Map<K,V> of(K k1, V v1, K k2, V v2, ..., K k10, V v10) static <K,V> Map<K,V> ofEntries(Map.Entry<? extends K, ? extends V>... entries) static <K,V> Map.Entry<K,V> entry(K k, V v)
For example,
import java.util.Map; public class J9MapOfEntryTest { public static void main(String[] args) { // JDK 9 // Using Map.of() factory method for 0 to 10 key-value pairs Map<String, Integer> nameAgePairs = Map.of("Peter", 60, "John", 16); System.out.println(nameAgePairs); //{John=16, Peter=60} System.out.println(nameAgePairs.get("John")); //16 // Unmodifiable //nameAgePairs.put("Paul", 18); //java.lang.UnsupportedOperationException // Using Map.ofEntries() and Map.entry() factory methods for an arbitrary number of key-value pairs Map<String, Double> coffeePrices = Map.ofEntries( Map.entry("espresso", 1.1), Map.entry("latte", 2.2), Map.entry("cappuccino", 3.3) ); System.out.println(coffeePrices); //{espresso=1.1, cappuccino=3.3, latte=2.2} } }
List.copyOf(), Set.copyOf() and Map.copyOf() (JDK 10)
JDK 10 introduces static
factory method List.copyOf()
, Set.copyOf()
, Map.copyOf()
to create a unmodifiable instance from another collection. The signatures are:
// Construct an unmodifiable instance, copying from the given object // Interface java.util.List<E> static <E> List<E> copyOf(Collection<? extends E> c) // Interface java.util.List<E> static <E> Set<E> copyOf(Collection<? extends E> c) // Interface java.util.Map<K,V> static <K,V> Map(K,V> copyOf(Map<? extends K, ? extends V> map)
For example,
import java.util.List; import java.util.Set; import java.util.Map; public class J10ListSetMapCopyOfTest { // JDK 10 public static void main(String[] args) { List<String> sourceLst = List.of("apple", "orange"); // 0 to 10 elements only List<String> fruitLst = List.copyOf(sourceLst); System.out.println(fruitLst); //[apple, orange] // Unmodifiable //fruitLst.add("banana"); //java.lang.UnsupportedOperationException Set<String> fruitSet = Set.copyOf(sourceLst); System.out.println(fruitSet); //[orange, apple] Map<String, Double> mapSource = Map.of("tea", 1.1, "coffee", 2.2); Map<String, Double> mapCopy = Map.copyOf(mapSource); System.out.println(mapCopy); //{coffee=2.2, tea=1.1} } }
Interface Collection<E> Enhancements (JDK 8, 11)
JDK 8 introduces these new default
methods:
- To complement the existing
abstract remove(Object)
andremoveAll(Collection)
, JDK 8 introducesdefault removeIf(Predicate)
to remove all the elements that match thePredicate
.
The signatures are:// Interface java.util.Collection<E> // Before JDK 8 abstract boolean remove(Object o) abstract boolean removeAll(Collection<?> c) // JDK 8 default boolean removeIf(Predicate<? super E> filter)
For example,import java.util.Collection; import java.util.List; import java.util.ArrayList; public class J8CollectionRemoveIfTest { // JDK 8 public static void main(String[] args) { Collection<String> fruits = new ArrayList<>(); // modifiable collection fruits.add("apple"); fruits.add("orange"); fruits.add("banana"); System.out.println(fruits); //[apple, orange, banana] // Pre-JDK 8 abstract remove(anObject) to remove anObject System.out.println(fruits.remove("apple")); //true System.out.println(fruits); //[orange, banana] // Pre-JDK 8 abstract removeAll(Collection) to remove those in Collection Collection<String> fruitsToRemove = List.of("orange", "pear"); // JDK 9 unmodifiable collection System.out.println(fruitsToRemove); //[orange, pear] System.out.println(fruits.removeAll(fruitsToRemove)); //true System.out.println(fruits); //[banana] // JDK 8 introduces default removeIf(Predicate) to remove those match Predicate System.out.println(fruits.removeIf(s -> s.charAt(0) == 'z')); //false System.out.println(fruits); //[banana] System.out.println(fruits.removeIf(s -> s.charAt(0) == 'b')); //true System.out.println(fruits); //[] } }
- To support Stream API, JDK 8 introduces
default
stream()
andparallelStream()
to create a sequential and parallelStream
from aCollection
. - To support parallel stream, JDK 8 introduces a
default spliterator()
to create aSpliterator
for traversing and partitioning elements of a source. Similar to the sequentialIterator
,Spliterator
was designed to support efficient parallel traversal.
JDK 11 introduces these new default
methods:
- To complement
toArray()
which creates anObject[]
andtoArray(T[])
which creates aT[]
, JDK 11 introducestoArray(IntFunction<T[]> generator)
to use the givengenerator
to allocate the returned fix-sized array, based on the size ofCollection
. Recall that arrays have fixed size, whileCollection
s are dynamically allocated.
The signatures are:// Interface java.util.Collection<E> // Before JDK 11 Object[] toArray() <T> T[] toArray(T[] a) // JDK 11 default <T> T[] toArray(IntFunction<T[]> arrayGenerator)
For example,import java.util.List; import java.util.Collection; import java.util.Arrays; public class J8CollectionToArrayTest { // JDK 8 public static void main(String[] args) { Collection<String> fruits = List.of("apple", "orange", "banana"); // JDK 9 unmodifiable list System.out.println(fruits); //[apple, orange, banana] // Pre-JDK 11 toArray() to create a fix-sized "Object" array Object[] fruitObjArray = fruits.toArray(); // array length based on fruits.size() System.out.println(fruitObjArray.length); //3 System.out.println(Arrays.toString(fruitObjArray)); //[apple, orange, banana] // Pre-JDK 11 toArray(T[]) to create an T[]. Need to pre-allocated a fix-sized array. String[] fruitStrArray1 = new String[fruits.size()]; System.out.println(fruitStrArray1.length); //3 fruits.toArray(fruitStrArray1); System.out.println(Arrays.toString(fruitStrArray1)); //[apple, orange, banana] // JDK 11 introduces toArray(arrayGenerator) to simplify the above String[] fruitStrArray2 = fruits.toArray(String[]::new); System.out.println(fruitStrArray2.length); //3 System.out.println(Arrays.toString(fruitStrArray2)); //[apple, orange, banana] } }
Interface Iterator<E> Enhancements (JDK 8)
JDK 8 added these default
methods:
default forEachRemaining​(Consumer)
to process the rest of elements. The signature is:// Interface java.util.Iterator<E> default void forEachRemaining(Consumer< super E> action)
For example,import java.util.List; import java.util.Iterator; public class J8IteratorTest { public static void main(String[] args) { List<String> coffees = List.of("espresso", "latte", "cappuccino"); // JDK 9 unmodifiable List System.out.println(coffees); //[espresso, latte, cappuccino] // JDK 8 introduces .forEachRemaining​(Consumer) Iterator<String> iter = coffees.iterator(); iter.next(); // consume the first element iter.forEachRemaining(System.out::println); //latte //cappuccino } }
Interface Map(K,V) Enhancements (JDK 8, 9, 10)
JDK 8 introduces new default
methods to java.util.Map(K,V)
:
- To complement the existing
abstract get(key)
, JDK 8 introducesdefault getOrDefault(key, defaultValue)
to provide a default value if key is not present. - To complement the existing
abstract put(key,value)
, JDK 8 introducesdefault putIfAbsent(key,value)
method to prevent accidental update. - To complement the existing
abstract remove(key)
, JDK 8 introducesdefault remove(key, value)
to remove matching key-value pair. - JDK 8 introduces
default forEach(BiConsumer)
to consume each key-value pair of the map. - JDK 8 supports "Stream on Map" via
entrySet().stream()
. - JDK 8 introduces
default compute(key, BiFunction)
,computeIfPresent(key, BiFunction)
,computeIfAbsent(key, Function)
to perform mapping of value based on key. - JDK 8 introduces
default replace(key, newValue)
,replace(key, oldValue, newValue)
to replace the value based on key. - JDK 8 introduces
default merge(key, newValue, BiFunction)
to merge the value based on key.
The signatures are:
// Interface java.util.Map(K,V) default void forEach(BiConsumer<? super K, ? super V> action) // Perform action on each key-value pair default V getOrDefault(Object key, V defaultValue) // Existing: abstract V get(Object key) default V putIfAbsent(K key, V value) // Existing: abstract V put(K key, V value) // Existing: abstract void putAll(Map<? extends K, ? extends V> m) default boolean remove(Object key, Object value) // Existing: abstract V remove(Object key) default V replace(K key, V value) default boolean replace(K key, V oldValue, V newValue) default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) default V merge(K key, V value, BiFunction<? super K, ? super V, ? extends V> remappingFunction) default V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) default V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
JDK 9 introduces these new static
methods (see above):
static of(k1, v1, k2, v2,...)
method to construct an unmodifiable map, for 1 to 10 key-value pairs.static ofEntries()
andentry()
to construct an un unmodifiable map of arbitrary number of key-value pairs.
JDK 10 introduces these new static
methods (see above):
static copyOf(Map)
method to copy the givenMap
to a new unmodifiable map.
For example,
import java.util.Map; import java.util.HashMap; import java.util.OptionalDouble; public class J8MapTest { public static void main(String[] args) { // Use JDK 9 static of() to construct a "unmodifiable" Map for 1 to 10 key-value pairs Map<String, Double> fruitPrices = Map.of("apple", 1.1, "orange", 2.2, "banana", 3.3); System.out.println(fruitPrices); //{apple=1.1, orange=2.2, banana=3.3} //fruitPrices.put("apple", 4.4); //java.lang.UnsupportedOperationException // Pre-JDK 8 System.out.println(fruitPrices.isEmpty()); //false System.out.println(fruitPrices.containsKey("banana")); //true System.out.println(fruitPrices.containsValue(0.0)); //false // In additional to abstract get(key), JDK 8 introduces default getOrDefault(key) System.out.println(fruitPrices.get("banana")); //3.3 System.out.println(fruitPrices.get("pear")); //null System.out.println(fruitPrices.getOrDefault("pear", 99.99)); //99.99 // Use JDK 9 static entry() and ofEntries() to construct a "unmodifiable" Map // for an arbitrary number of key-value pairs Map<String, Double> teaPrices = Map.ofEntries( Map.entry("green tea", 1.1), Map.entry("black tea", 2.2)); System.out.println(teaPrices); //{green tea=1.1, black tea=2.2} // Construct a modifiable map with a chosen implementation (Pre-JDK 8) Map<String, Double> coffeePrices = new HashMap<>(); // modifiable coffeePrices.put("cappuccino", 5.5); coffeePrices.put("espresso", 6.6); System.out.println(coffeePrices); //{espresso=6.6, cappuccino=5.5} coffeePrices.put("cappuccino", 7.7); // update System.out.println(coffeePrices); //{espresso=6.6, cappuccino=7.7} // In additional to abstract put(), JDK 8 introduces default putIfAbsent() coffeePrices.putIfAbsent("cappuccino", 8.8); // no update System.out.println(coffeePrices); //{espresso=6.6, cappuccino=7.7} // JDK 8 introduces default forEach(BiConsumer) coffeePrices.forEach((k, v) -> System.out.println(k + ":" + v)); //espresso:6.6 //cappuccino:7.7 // JDK 8 introduces Stream API // Stream API does not support Map directly. Need to get a Set view via .entrySet(). OptionalDouble maxPrice = coffeePrices .entrySet() // Returns a Set view Set<Map.Entry<K,V>> of this map .stream() .mapToDouble(Map.Entry::getValue) .max(); System.out.println(maxPrice); //OptionalDouble[7.7] // JDK 8 introduces default compute(key, BiFunction) to compute (update) // a newValue (instead of get() and put()) coffeePrices.compute("cappuccino", (k, v) -> v + 1.0); System.out.println(coffeePrices); //{espresso=6.6, cappuccino=8.7} // Also default computeIfPresent(key, aBiFunction(key,oldValue)) coffeePrices.computeIfPresent("cappuccino", (k, v) -> v + 1.0); System.out.println(coffeePrices); //{espresso=6.6, cappuccino=9.7} // Also default computeIfAbsent(key, Function(key)) coffeePrices.computeIfAbsent("latte", k -> 8.8); System.out.println(coffeePrices); //{espresso=6.6, cappuccino=9.7, latte=8.8} System.out.println(coffeePrices.computeIfAbsent("latte", k -> 9.9)); // 8.8 (no update) // In additional to abstract remove(key), JDK 8 introduces default remove(key, value) // for removing a matching key-value pair System.out.println(coffeePrices.remove("latte", 0.0)); //false System.out.println(coffeePrices.remove("latte", 8.8)); //true System.out.println(coffeePrices); //{espresso=6.6, cappuccino=9.7} // JDK 8 introduces default replace(key, newValue) if key exists System.out.println(coffeePrices.replace("espresso", 9.9)); //6.6 (return old value) System.out.println(coffeePrices); //{espresso=9.9, cappuccino=9.7} System.out.println(coffeePrices.replace("latte", 9.9)); //null (old value) // Also default replace(key, oldValue, newValue) System.out.println(coffeePrices.replace("espresso", 9.9, 11.1)); //true System.out.println(coffeePrices); //{espresso=11.1, cappuccino=9.7} System.out.println(coffeePrices.replace("espresso", 0.0, 12.2)); //false (no change) // JDK 8 introduces default merge(key, newValue, BiFunction) // If key exists, replace by the output of BiFunction System.out.println(coffeePrices.merge("espresso", 1.1, (oldValue, v) -> oldValue + v)); //12.2 (replace) System.out.println(coffeePrices); //{espresso=12.2, cappuccino=9.7} // If key does not exist, put an entry (key, newValue), ignore BiFunction System.out.println(coffeePrices.merge("latte", 1.1, (oldValue, v) -> oldValue + v)); //1.1 System.out.println(coffeePrices); //{espresso=12.2, cappuccino=9.7, latte=1.1} // JDK 10 introduces static copyOf(Map) to copy to a map to a new unmodifiable map Map<String, Double> newCoffeePrices = Map.copyOf(coffeePrices); System.out.println(newCoffeePrices); //{latte=1.1, espresso=12.2, cappuccino=9.7} (unmodifiable) } }
Interface java.util.Comparator<T> Enhancements
[TODO]
Others
- more.
Enhancements to the Collection Framework by JDK Releases
JDK 5 Enhancements to the Collection Framework
New Language Features
JDK 5 introduces MAJOR upgrades to the Java Language. It introduces many new language features, which are applicable to collection framework:
- Generics: support collection of generic type. See "Generics".
- Auto-boxing/Auto-Unboxing of primitive types (such as
int
,double
) to their wrapper types (such asInteger
,Double
). SinceCollection
does not hold primitives, the auto-boxing/auto-unboxing greatly simplifies the conversion. See "Auto-boxing/Auto-Unboxing". - Enhanced for-each loop: Simplifies looping through all elements of a
Collection
. See "Enhance for-each loop". - Annotations
- Enumerations
- Varargs (Variable number of arguments)
static import
statement
New API Packages
- New package
java.util.concurrent
to support concurrent (parallel) programming, with thread-safe collection classes that allow to be modified, such asCopyOnWriteArrayList
,ConcurrentHashMap
,CopyOnWriteArraySet
.
JDK 6 Enhancements to the Collection Framework
[TODO]
JDK 7 Enhancements to the Collection Framework
New Language Features
- JDK 7 improves type inference for generic instant creation with the diamond operator
<>
. For example,// Before JDK 7 // Construct an instance of Generic List<String> lst = new ArrayList<String>(); // JDK 7 // The type can be omitted and inferred from the variable declaration List<String> lst = new ArrayList<>();
JDK 8 Enhancements to the Collection Framework
New Language Features
JDK 8 introduces MAJOR upgrades to the Java Language. It introduces many new language features (See "Lambda Expression, Streams and Functional Programming"):
public default
andpublic static
methods in interface.- Lambda Expressions: shorthand for creating an instance that implements a single-abstract-method interface via an anonymous inner class.
New API Packages
JDK 8 introduces these new API packages, which enhances collection framework (See "Lambda Expression, Streams and Functional Programming"):
- Stream API: in package
java.util.stream
. - Function API: in package
java.util.function
.
New Classes/Interfaces
JDK 8 introduces these new classes/interfaces to the Collection framework:
- Added class
java.util.Optional<T>
, which may contain a value or nothing, to avoid returningnull
that may triggerNullPointerException
.
New Interface default/static Methods
JDK 8 enhances the Collection Library:
- Interface
java.lang.Iterable<T>
(supertype of Interfacejava.util.Collection<E>
):default void forEach(Consumer<? super T> action) // Perform action on each element of the Iterable (Collection)
- Interface
java.util.Collection<E>
:// Supporting Stream API default Stream<E> stream() // Creates a sequential stream default Stream<E> parallelStream() // Creates a parallel stream default Spliterator<E> spliterator() // Creates a Spliterator to support efficient parallel traversal // Others default boolean removeIf(Predicate<? super E> filter) // Removes elements that satisfy the Predicate
- Interface
java.util.Iterator<T>
:default void forEachRemaining(Consumer<? super E> action) // Performs action on the rest of elements default void remove() // Remove the last element returned by this Iterator
- Interface
java.util.Map<K,V>
:default void forEach(BiConsumer<? super K, ? super V> action) // Perform action on each key-value pair default V getOrDefault(Object key, V defaultValue) // Existing: abstract V get(Object key) default V putIfAbsent(K key, V value) // Existing: abstract V put(K key, V value) // Existing: abstract void putAll(Map<? extends K, ? extends V> m) default boolean remove(Object key, Object value) // Existing: abstract V remove(Object key) default V replace(K key, V value) default boolean replace(K key, V oldValue, V newValue) default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) default V merge(K key, V value, BiFunction<? super K, ? super V, ? extends V> remappingFunction) default V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) default V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
JDK 9 Enhancements to the Collection Framework
New Language Features
JDK 9 introduces these new language features:
private
andprivate static
methods in interface.
New Interface default/static Methods
JDK 9 introduces these methods:
- Interfaces
java.util.List<E>
,java.util.Set<E>
,java.util.EnumSet<E>
:// Construct an unmodifiable instance, with 0 to 10 elements static <E> List<E> of(e1, e2, ...) static <E> Set<E> of(e1, e2, ...)
- Interface
java.util.Map<K,V>
:// Constructs an unmodifiable instance of Map, with 0 to 10 key-value pairs static <K,V> Map(K,V) of(k1, v1, k2, v2, ...) // Constructs an unmodifiable instance of Map, with arbitrary number of key-value pairs static <K,V> Map(K,V) ofEntries(Map.Entry<? extends K, ? extends V>... entries) static <K,V> Map.Entry<K,V> entry(K k, V v)
JDK 10 Enhancements to the Collection Framework
New Interface default/static Methods
JDK 10 introduces these methods:
- Interfaces
java.util.List<E>
,java.util.Set<E>
,java.util.EnumSet<E>
:// Construct an unmodifiable instance, copying from the given Collection static <E> List<E> copyOf(Collection<? extends E> c) static <E> Set<E> copyOf(Collection<? extends E> c)
- Interface
java.util.Map<K,V>
:// Construct an unmodifiable instance of Map, copy from the given Map static <K,V> Map(K,V> copyOf(Map<? extends K, ? extends V> map)
JDK 11 Enhancements to the Collection Framework
New Interface default/static Methods
JDK 11 enhances the Collection Libraries:
java.util.Collection<E>
:default <T> T[] toArray(IntFunction<T[]> generator) // Returns an array containing all elements in this Collection, // using the provided generator function to allocate the returned fixed-size array // Existing: Object[] toArray() // Existing: <T> T[] toArray(T[] a) // Similar function in java.util.stream.Stream
JDK 12 Enhancements to the Collection Framework
[TODO]
JDK 13 Enhancements to the Collection Framework
[TODO]
JDK 14 Enhancements to the Collection Framework
[TODO]
LINK TO JAVA REFERENCES & RESOURCESMore References
- Java Online Tutorial on "Generics" @ http://docs.oracle.com/javase/tutorial/extra/generics/index.html.
- Java Online Tutorial on "Collections" @ http://docs.oracle.com/javase/tutorial/collections/index.html.