简体   繁体   中英

Inline comparator vs custom comparator in Java

When sorting a list, is there any performance difference between using a java Comparator in-line (with an anonymous inner class) vs implementing a separate custom Comparator class?

1.

public class SortByErrorComparator implements Comparator<WorkflowError> {
    public int compare(WorkflowError obj1, WorkflowError obj2) {
        return obj1.getErrorCode().compareTo(obj2.getErrorCode());
    }
}
Collections.sort(list, new SortByErrorComparator()) ;

2.

Collections.sort(list, new Comparator<WorkflowError>() {
    public int compare(WorkflowError obj1, WorkflowError obj2) {
        return obj1.getErrorCode().compareTo(obj2.getErrorCode());
    }
});

Also, when will the compare() method be invoked?

There's also option 3 - a lambda Function :

Collections.sort(list, (a, b) -> a.getErrorCode().compareTo(b.getErrorCode()));

which should be about 2 x faster, according to this benchmark data .

... or (thanks to @JB Nizet ) option 4:

list.sort(Comparator.comparing(WorkflowError::getErrorCode))

There's shouldn't be any performance difference between the two variations, since anonymous classes should produce identical byte code as regular classes (assuming they have the same source code). The only difference is that they'll have a generated name.

The compare method will be invoked by Collections.sort whenever it needs to compare two elements of the List to be sorted.

I made a little test and found no difference (just in some small run the inline comparator shows a slightly better performace). This is the code used to make the test:

public class ComparatorTest {

    private static final int MAX = 1000000;
    private static final int RUN = 10000;

    public static void main(String[] args) {

        List<A> list = new ArrayList<A>();

        long externalComparatorClassTotalTime = 0;
        long inlineCompartorTotalTime = 0;

        for (int i = RUN; i > 0; i--) {
            init(list);
            externalComparatorClassTotalTime += externalComparatorClassTest(list);
            init(list);
            inlineCompartorTotalTime += inlineCompartorTest(list);
        }

        System.out.format("List with %d elements and %d runs%n", MAX, RUN);
        System.out.println("external Comparator class average millis: " + externalComparatorClassTotalTime / RUN);
        System.out.println("inline Comparator class average millis: " + inlineCompartorTotalTime / RUN);
    }

    private static void init(List<A> list) {
        list.clear();
        for (int i = MAX; i > 0; i--) {
            list.add(new A(i));
        }
    }

    private static long inlineCompartorTest(List<A> secondList) {
        long start = System.currentTimeMillis();

        Collections.sort(secondList, new Comparator<A>() {
                public int compare(A obj1, A obj2) {
                    return obj1.getVal().compareTo(obj2.getVal());
                }
        });

        return System.currentTimeMillis() - start;
    }

    private static long externalComparatorClassTest(List<A> firstList) {
        long start = System.currentTimeMillis();

        Collections.sort(firstList, new MyComparatorOne());

        return System.currentTimeMillis() - start;
    }
}

Comparator class:

public class MyComparatorOne implements Comparator<A> {
    public int compare(A obj1, A obj2) {
        return obj1.getVal().compareTo(obj2.getVal());
    }
}

and the output is:

List with 1000000 elements and 10000 runs
external Comparator class average millis: 3
inline Comparator class average millis: 3

If you have several invocation to the comparator keeping an instance of it would be helpful

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM