Fail fast or Fail early is a software engineering concept that tries to prevent complex problems happening by stopping execution as soon as something that shouldn't happen, happens. In a previous blog post and presentation I go more into detail about the merits of this approach, in this blog post I will just detail another use of this idea in Java 8.
In Java, Iterators returned by Collection classes e.g. ArrayList, HashSet, Vector etc are fail fast.
This means, if you try to add() or remove() from the underlying data structure while iterating it
you get a ConcurrentModificationException.
Let's see:
import static java.util.Arrays.asList; ListIn Java 8u20, the Collections.sort() API is also fail fast. This means you can't invoke it inside an iteration either. For example:ints = new ArrayList<>(asList(1,2,3,4,5,6,9,15,67,23,22,3,1,4,2)); for (Integer i: ints) { // some code ints.add(57); // throws java.util.ConcurrentModificationException }
import static java.util.Arrays.asList; ListThis makes sense. Iterating over a data structure and sorting it during the iteration is not only counter intuitive but something likely to lead to unpredictable results. Now, you can get away with this and not get the exception if you have break immediately after the sort invocation.ints = new ArrayList<>(asList(1,2,3,4,5,6,9,15,67,23,22,3,1,4,2)); for (Integer i: ints) { // some code Collections.sort(ints); // throws java.util.ConcurrentModificationException }
import static java.util.Arrays.asList; ListBut, that's hardly great code. Try to avoid old skool iterations and you use Lambdas when you can. But, if you are stuck, just do the sort when outside the iterationints = new ArrayList<>(asList(1,2,3,4,5,6,9,15,67,23,22,3,1,4,2)); for (Integer i: ints) { // some code Collections.sort(ints); // throws java.util.ConcurrentModificationException break; }
import static java.util.Arrays.asList; Listor use a data structure which sorts when you add.ints = new ArrayList<>(asList(1,2,3,4,5,6,9,15,67,23,22,3,1,4,2)); Collections.sort(ints); for (Integer i: ints) { // some code }
This new behaviour of the Collections.sort() API came in Java 8 release 20. It is worth having a look at the specific section that details the change in the API:
"
Area: core-libs/java.util.collections
Synopsis: Collection.sort defers now defers to List.sort
Previously
Collection.sort
copied the elements of the list to sort into an array, sorted that
array, then updated list, in place, with those elements in the array,
and the default method List.sort
deferred to Collection.sort. This was a non-optimal arrangement.From 8u20 release onwards
Collection.sort
defers to List.sort
. This means, for example, existing code that calls Collection.sort
with an instance of ArrayList
will now use the optimal sort implemented by ArrayList."
I think it would have helped if Oracle were a little more explicit here on how this change could cause runtime problems. Considering everybody uses the Collections framework if an API that previously didn't throw a exception now can for the same situation (bad code and all that it is), it is better if the release notes made it easier for developers to find that information out.