1. Introduction
In this article we will explore the methods that were added to List interface in and after Java 8 release. No new methods were added in Java 11 and 12.
2. Content
These methods are really convenient for us as without this we used our own code to achieve this functionality and maintained that code. Now it is easier as JDK provides this code to us. We will take a look at pre java 8 ways to achieve the below mentioned functionality of these methods.
default void replaceAll(UnaryOperator<E> operator) default void sort(Comparator<? super E> c) default Spliterator<E> spliterator() static <E> List<E> of() static <E> List<E> of(E e1) static <E> List<E> of(E e1, E e2) static <E> List<E> of(E e1, E e2, E e3) static <E> List<E> of(E e1, E e2, E e3, E e4) static <E> List<E> of(E e1, E e2, E e3, E e4, E e5) static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6) static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7) static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8) static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) static <E> List<E> of(E... elements) static <E> List<E> copyOf(Collection<? extends E> coll)
There are several methods added in Collection and an Iterable interface too. These methods are common to the entire Collection Framework. They are discussed here. The methods discussed in Collection Interface article are toArray(), removeIf(), spliterator(), stream() and parallelStream().
This article will focus on methods of List interface.
3. default void replaceAll(UnaryOperator<E> operator)
replaceAll method was added in Java 8.
replaceAll is a default method which does in-place bulk mutation operation.
in-place operation means the source of input is operated upon and modified.
Bulk mutation means the entire collection or part of the collection is mutated.
Let us take a minute here to understand what does UnaryOperator mean. UnaryOperator means a function that accepts input of type T and returns result of type T. UnaryOperator is a specialized version of the Function interface where input and the output type are the same.
List<Integer> numbers = new ArrayList<>(); numbers.add(1); numbers.add(2); numbers.add(3); numbers.add(4); numbers.add(5); numbers.add(6); numbers.replaceAll(val -> val * 10); // expected output List<Integer> output = Arrays.asList(10, 20, 30, 40, 50, 60); Assert.assertEquals(output, numbers);
replaceAll example with String :
List<String> names = new ArrayList<>(); names.add("Jon"); names.add("Sansa"); names.add("Rickon"); names.add("Arya"); names.add("Robb"); names.replaceAll(String::toUpperCase); // expected output List<String> output = Arrays.asList("JON", "SANSA", "RICKON", "ARYA", "ROBB"); Assert.assertEquals(output, names);
Pre Java 8 you can achieve the same functionality using ListIterator.
List<Integer> numbers = new ArrayList<>(); numbers.add(1); numbers.add(2); numbers.add(3); numbers.add(4); numbers.add(5); numbers.add(6); for (ListIterator<Integer> iter = numbers.listIterator(); iter.hasNext();) { iter.set(iter.next() * 10); } // expected output List<Integer> output = Arrays.asList(10, 20, 30, 40, 50, 60); Assert.assertEquals(output, numbers);
You are now allowed to change the type of result. In order to change the type use Stream.
Example :
List<Long> longs = numbers .stream() .map(Long::valueOf) .collect(Collectors.toList());
4.default void sort(Comparator<? super E> c)
Sort method was added in Java 8.
sort method sorts the collection the elements in the List. Prior to Java 8 we had to use Collections class’s sort method.
16 new methods were added in the Comparator interface in Java 8. Read about them in this eBook. This eBook contains several different utilities of Comparators too.
sort() method accepts Comparator as an argument. If the Comparator is passed as null then List is sorted based on natural ordering else it uses Comparator’s ordering.
Be cautious when using natural ordering because if the class doesn’t implement the Comparable interface then the method will throw ClassCastException in runtime.
One fine detail about sort method : Collections.sort method used to work a bit differently. It used to be a three step process.
1. Copy the List into an array.
2. Sorts the array.
3. Sets the values in List back in sorted manner from array.
List.sort method does the same thing today. But the ArrayList class overrides the sort method and provides in-place sorting. So no additional memory is used and no copying of data elements take place.
Check this link for the implementation of sort method of Collections class. Now the Collections class’s sort method just calls List.sort(). If the instance of Collections.sort is ArrayList then no copying takes place, if the instance is LinkedList then the 3 step logic comes into picture.
Using List<String> and natural ordering.
List<String> names = new ArrayList<>(); names.add("Jon"); names.add("Sansa"); names.add("Rickon"); names.add("Arya"); names.add("Robb"); names.sort(null); List<String> output = Arrays.asList("Arya", "Jon", "Rickon", "Robb", "Sansa"); Assert.assertEquals(output, names);
Using List<Transaction>. Transaction class doesn’t implement the Comparable interface. As expected, calling sort method will throw a ClassCastException.
Assertions.assertThrows(ClassCastException.class, () -> { List<Transaction> names = new ArrayList<>(); names.addAll(Transactions.getDataSet()); names.sort(null); });
Using Comparator interface’s comparing method you can provide a strategy to sort the List. In our case we are sorting using the Transaction date.
List<Transaction> names = new ArrayList<>(); names.addAll(Transactions.getDataSet()); names.sort(Comparator.comparing( Transaction::date, LocalDate::compareTo));
Pre Java 8 way to sort a List was to use Collections class.
// names is List<String>. String is Comparable so we can use sort method // without Comparator. Collections.sort(names); // Transaction Comparator. Comparator<Transaction> DATE_COMPARATOR = new Comparator<Transaction>() { @Override public int compare(Transaction t1, Transaction t2) { return t1.date().compareTo(t2.date()); } }; Collections.sort(transactions, DATE_COMPARATOR);
5. default Spliterator<E> spliterator()
spliterator() method was added in Java 8. Spliterator is responsible for traversing and partitioning the elements of the source. It is used extensively in Stream APIs that were introduced in Java 8.
Don’t use this method unless you have a very good reason to use it. To use Stream API on any collection just call stream() method on it. stream() method is defined in Collection interface.
To create a stream from the spliterator() method you will need a StreamSupport class.
Example : Given List<String> return Map<String, Integer> where entry defines string – length of string.
List<String> names = new ArrayList<>(); names.add("Jon"); names.add("Sansa"); names.add("Rickon"); names.add("Arya"); names.add("Robb"); Spliterator<String> spliterator = names.spliterator(); Map<String, Integer> collect = StreamSupport .stream(spliterator, false) .collect( Collectors.toMap( name -> name, name -> name.length())); Output {Sansa=5, Arya=4, Jon=3, Robb=4, Rickon=6}
6. Static factories
Static factories i.e of() and copyOf() methods were added in Java 9 and Java 10 respectively.
Multiple static factory methods are provided for to create Immutable instances of List. The returned instances are not ArrayList objects. It is a customized implementation in class java.util.ImmutableCollections. Take a look at the class code, it is really good.
null elements are disallowed in static factories. This is a big improvement. While iterating we usually have to make sure that the element we are processing is not null. If we are using these static factories then there won’t be any null elements in List.
6.1 of static factories
All the static factories are immutable. If you attempt to modify the List structurally i.e. to add, remove or set the elements it throws UnsupportedOperationException.
If the elements in the List itself are mutable then the List’s contents will change.
Method | What does it do? |
static <E> List<E> of() | Returns unmodifiable empty list |
static <E> List<E> of(E e1) | Returns an unmodifiable list with one element |
static <E> List<E> of(E e1, E e2) | Returns an unmodifiable list with two elements |
static <E> List<E> of(E e1, E e2, E e3) | Returns an unmodifiable list with three elements |
static <E> List<E> of(E e1, E e2, E e3, E e4) | Returns an unmodifiable list with four elements |
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5) | Returns an unmodifiable list with five elements |
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6) | Returns an unmodifiable list with six elements |
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7) | Returns an unmodifiable list with seven elements |
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8) | Returns an unmodifiable list with eight elements |
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) | Returns an unmodifiable list with nine elements |
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) | Returns an unmodifiable list with 10 elements |
static <E> List<E> of(E… elements) | Returns an unmodifiable list with arbitrary numbers of elements |
// creates empty List<String> List.of(); // creates List<String> with 1 element List.of("Rickard Stark"); // creates List<String> with 2 elements List.of("Rickard Stark", "Lyarra Stark"); // creates List<String> with 3 elements List.of("Rickard Stark", "Lyarra Stark", "Brandon Stark"); // creates List<String> with 4 elements List.of("Rickard Stark", "Lyarra Stark", "Brandon Stark", "Eddard Stark"); // creates List<String> with 5 elements List.of("Rickard Stark", "Lyarra Stark", "Brandon Stark", "Eddard Stark", "Lyanna Stark"); // creates List<String> with 6 elements List.of("Rickard Stark", "Lyarra Stark", "Brandon Stark", "Eddard Stark", "Lyanna Stark", "Benjen Stark"); // creates List<String> with 7 elements List.of("Rickard Stark", "Lyarra Stark", "Brandon Stark", "Eddard Stark", "Lyanna Stark", "Benjen Stark", "Sansa Stark"); // creates List<String> with 8 elements List.of("Rickard Stark", "Lyarra Stark", "Brandon Stark", "Eddard Stark", "Lyanna Stark", "Benjen Stark", "Sansa Stark", "Robb Stark"); // creates List<String> with 9 elements List.of("Rickard Stark", "Lyarra Stark", "Brandon Stark", "Eddard Stark", "Lyanna Stark", "Benjen Stark", "Sansa Stark", "Robb Stark", "Bran Stark"); // creates List<String> with 10 elements List.of("Rickard Stark", "Lyarra Stark", "Brandon Stark", "Eddard Stark", "Lyanna Stark", "Benjen Stark", "Sansa Stark", "Robb Stark", "Bran Stark", "Rickon Stark"); // creates List<String> with varargs elements List.of("Rickard Stark", "Lyarra Stark", "Brandon Stark", "Eddard Stark", "Lyanna Stark", "Benjen Stark", "Sansa Stark", "Robb Stark", "Bran Stark", "Rickon Stark", "Arya Stark");
6.2 copyOf static factory
copyOf method was added in Java 10. copyOf method returns the unmodifiable list which comprises elements from a given collection. While the elements are copied over to the new List the iteration order of the specified List is maintained.
List<String> names = new ArrayList<>(); names.add("Jon"); names.add("Sansa"); names.add("Rickon"); names.add("Arya"); names.add("Robb"); List<String> namesCopy = List.copyOf(names); List<String> output = names; Assert.assertEquals(output, namesCopy);
7. Conclusion
List interface now has several different methods that can be used with any additional orchestration. All of these methods are simple, effective and elegant. That’s all on the addition to List interface. If you have any questions, post in the comments. If you like this article consider subscribing. I am giving away a free eBook. Grab it now.