I am using Java 8 (build 1.8.0_25), Netbeans 8.0.2 and am incorporating some of the Java 8 features into an existing app. Sorting and .forEach is not working so I have creat
The root cause of this problem is the use of a flawed implementation pattern in the IndirectList
class of EclipseLink JPA. (doc, source) This problem occurs in the 2.5 release family; it may also occur in other versions.
The problem is that this class both subclasses Vector
and has a reference to a Vector
instance. It attempts to delegate all method calls to that instance by overriding all of the methods of Vector
. This works fine, as long as no new methods are added to Vector
.
This happened in Java 8.
Java 8 added several new default methods to the Collection
, Iterable
, and List
interfaces, including:
forEach
parallelStream
removeIf
replaceAll
sort
spliterator
stream
Typically, adding default methods is safe, since they must be implemented in terms of other existing methods. However, it is usually a good idea for implementing classes to override default methods for reasons of efficiency. The Java 8 Vector
implementation added several overrides of these default methods. These work fine if you have an instance of the actual Vector
class. The IndirectList
class doesn't override these methods, so the delegation path that it attempted to set up doesn't work for these methods. Instead, the ordinary Vector
implementations are used. Unfortunately, the IndirectList
methods don't keep the superclass state up to date, so the Vector
implementations of the methods all behave as if the Vector
is empty.
Vector
overrides forEach
, removeIf
, replaceAll
, sort
, and spliterator
. The parallelStream
and stream
default methods are implemented in terms of spliterator
, so the same thing happens to them. Essentially, none of the new default methods on collections will work if they are used on an IndirectList
implementation retrieved from EclipseLink JPA.
Note that this problem also occurs with Collections.sort(indirectList)
. This method simply calls the indirectList.sort()
method, so it encounters exactly the same problem as described above.
Some potential workarounds for this problem are listed in this answer.
For further information about the status of EclipseLink, see EclipseLink JPA bugs 433075 and 446236.
For further information about the pitfalls of this implementation pattern, see Joshua Bloch's book Effective Java, Second Edition, Item 16: Favor Composition over Inheritance.
Try changing the way you handle your lists. If you want to get the items of a list, sort them, and assign them to a new list, then instead, try this:
List<RegistrationItem> sortedItems = reg.getRegistrationItem().stream()
.sorted((a, b)-> a.getId().compareTo(b.getId()))
.peek(System.out::println)
.collect(Collectors.toList());
Using forEach() and add, and then sorting is really inefficient. Using the streams API allows you to do all of this in a much more parallel-friendly manner, with less code.
The only thing that can go wrong in this code is that reg.getRegistrationItem()
doesn't return a List<RegistrationItem>
or inside the comparator if a RegistrationItem
's getId()
returns null
.
You can make the comparator more null safe by layering null-safe comparators to the comparator. For instance, the following code treats null
as a lower value than any other:
List<RegistrationItem> item = regI.stream()
.sorted(Comparator.nullsFirst(
Comparator.comparing(RegistrationItem::getId,
Comparator.nullsFirst(String::compareTo))))
.peek(System.out::println)
.collect(Collectors.toList());