Core Java

Java8 Sorting – Performance Pitfall

Java 8 brings all the goodness of lambdas to enable us to program using a declarative style. But is it really free? And should we be concerned about the price we have to pay for the new programming goodies?

Here’s an example where we might have to worry.

Consider sorting instances of this simple class:
 
 
 
 
 

private static class MyComparableInt{
        private int a,b,c,d;

        public MyComparableInt(int i) {
            a = i%2;
            b = i%10;
            c = i%1000;
            d = i;
        }

        public int getA() { return a; }
        public int getB() { return b; }
        public int getC() { return c; }
        public int getD() { return d; }
}

We can sort using the new Java 8 declarative syntax as below:

List<MyComparableInt> mySortedComparableList = 
      myComparableList.stream()
       .sorted(Comparator.comparing(
         MyComparableInt::getA).thenComparing(
         MyComparableInt::getB).thenComparing(
         MyComparableInt::getC).thenComparing(
         MyComparableInt::getD))
       .collect(Collectors.toList());

Or we can sort the old way (still using lambdas) using this code:

List<MyComparableInt> mySortedComparableList = 
         myComparableList.stream()
           .sorted(MyComparableIntSorter.INSTANCE)
           .collect(Collectors.toList());


 public enum MyComparableIntSorter implements 
     Comparator<MyComparableInt>{
        INSTANCE;

        @Override
        public int compare(MyComparableInt o1, MyComparableInt o2) {
            int comp = Integer.compare(o1.getA(), o2.getA());
            if(comp==0){
                comp = Integer.compare(o1.getB(), o2.getB());
                if(comp==0){
                    comp = Integer.compare(o1.getC(), o2.getC());
                    if(comp==0){
                        comp = Integer.compare(o1.getD(), o2.getD());
                    }
                }
            }
            return comp;
        }
 }

When I ran this test with 10m objects the sort took ~6.5s using the declarative syntax but only 1.5s using the old syntax. That’s almost 4 times as long!

So where is the time going? Presumably it’s in the overhead for marshalling between the thenComparing methods.

Interestingly, if you try exactly the same test but replace int for String the times change as follows. Again for 10m objects, using the new syntax takes ~11.5s whilst the old syntax takes ~7s. Using String, when the marshalling is less significant the new syntax takes only 1.5 times as long as the old syntax.

In summary, whilst the new syntax looks great and is wonderfully expressive, if you are worried about performance you should stick to the old syntax.

Once again it seems that there is no such thing as a free lunch!

Reference: Java8 Sorting – Performance Pitfall from our JCG partner Daniel Shaya at the Rational Java blog.

Daniel Shaya

Daniel has been programming in Java since it was in beta. Working predominantly in the finance industry he has created real time trading and margin risk applications. He is currently a director at OpenHFT where we are building next generation Java low latency products.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

4 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
perceptron8
perceptron8
9 years ago

How about…?

List mySortedComparableList =
myComparableList.stream()
.sorted(Comparator.comparingInt(
MyComparableInt::getA).thenComparingInt(
MyComparableInt::getB).thenComparingInt(
MyComparableInt::getC).thenComparingInt(
MyComparableInt::getD))
.collect(Collectors.toList());

Andrey
9 years ago
Reply to  perceptron8

comparingInt/thenComparingInt seems to be a bit faster than comparing/thenComparing version.
However it’s still much slower than Comparable/Comparator approach.

http://pastebin.com/neV0vVfE

Jacob Zimmerman
9 years ago

Good stuff.
This doesn’t go for or against your point, but I just wanted to point out that the benefits of the first extend beyond more than readability and ease of use. What is probably the biggest benefit is the reuse factor. If you composed your Comparator from several small comparators, you could mix, match, and rearrange them as you please for other comparisons in other places.
This, of course, does not make your point wrong. It’s just another thing to consider when choosing how you want to do it.

Francesco
Francesco
9 years ago

what if you replace int with the wrapper type Integer?

Back to top button