Vinay Prajapati
Vinay Prajapati

Reputation: 7546

Using Comparator.comparingLong has any advantage over long.compare?

Out of curiosity if using Comparator in java8 style i.e. using Lambda expressions has any advantage over regular comparison i.e.

One method of sorting by id is:-

List sortedAcs = ac
.stream()
.sorted((a, b) -> Long.compare(a.getId(), b.getId())) //sorted by id here
.collect(Collectors.toList());

Other methods could be Java 8 way:-

List sortedAcs = ac
.stream()
.sorted(Comparator.comparingLong(AC::getId)) //sorted by id here
.collect(Collectors.toList());

Is there any performance benefit in later approach (java-8 method reference) over former approach?

Please help!!!

Upvotes: 6

Views: 10230

Answers (4)

velocity
velocity

Reputation: 2096

I was trying to sort a Map before having gotting a warning about the usage of

(entry1,entry2)->Long.compare(entry1.getKey(),entry2.getKey())

which should be replaced using Comparator.comparingLong and the result is really a better way to implement it:

Comparator.comparingLong(Map.Entry::getKey)

Inspecting the implementation of comparingLong i found out that it is basically the same implementation, only that it is cleaner and less time consuming.

return (Comparator<T> & Serializable)
(c1, c2) -> Long.compare(keyExtractor.applyAsLong(c1), keyExtractor.applyAsLong(c2))

Upvotes: 0

Vinay Prajapati
Vinay Prajapati

Reputation: 7546

So, here comes answer from one perspective i.e. performance.

Here is my code I used to test it:-

AC class:-

package com.test;

public class AC {

    private Long id;

    public AC(Long id) {
        this.id = id;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "AC{" +
                "id=" + id +
                '}';
    }
}

Main class:-

package test.java;

import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class Main {

    @org.openjdk.jmh.annotations.Benchmark
    public void measureName() {
        List<AC> acs = new ArrayList<>();

        acs.add(new AC(20l));
        acs.add(new AC(30l));
        acs.add(new AC(10l));
        acs.add(new AC(30l));
        acs.add(new AC(80l));
        acs.add(new AC(50l));
        acs.add(new AC(30l));
        acs.add(new AC(90l));
        acs.add(new AC(80l));
        acs.add(new AC(110l));

   /*     acs
                .stream()
                .sorted(Comparator.comparingLong(AC::getId)) //sorted by id here
                .collect(Collectors.toList());*/

        acs.stream()
                .sorted((a, b) -> Long.compare(a.getId(), b.getId())) //sorted by id here
                .collect(Collectors.toList());

    }

    public static void main(String[] args) {

        Options opt = new OptionsBuilder()
                .include(".*" + Main.class.getSimpleName() + ".*")
                .forks(1)
                .build();

        try {
            new Runner(opt).run();
        } catch (RunnerException e) {
            e.printStackTrace();
        }
    }
}

Putting below output using JMH for using Comparator.comparingLong:-

# Run complete. Total time: 00:00:40

Benchmark          Mode  Cnt        Score       Error  Units
Main.measureName  thrpt   20  4130836.283 ± 86675.431  ops/s

and for Long.compare below:-

# Run complete. Total time: 00:00:40

Benchmark          Mode  Cnt        Score        Error  Units
Main.measureName  thrpt   20  4106542.318 ± 146956.814  ops/s

If I go by these statistics Long.compare is somehow faster though difference is very minor.

Please feel free to put in comments your findings if any and I would try those too.

Upvotes: 1

Hoopje
Hoopje

Reputation: 12932

Any performence difference in the two code snippets will be negligable. And if you really need to optimize that code, then not using streams will probably give you a much greater performance boost than replacing that comparator.

The only criterion you should use to choose between the two variants is clarity: which one do you think conveys the intent of the code more clearly? In the end, this is a personal preference, depending on how fluent you are with the Java 8 features, among other things.

Personally, I find the second snippet more clear than the first one. The comparingLong method (and other comparingX methods) immediately tells me: here we're comparing objects based on the value of a (long-typed) attribute. In the first snippet, I first need parse the code to determine that that is indeed what happens.

Upvotes: 2

Eugene
Eugene

Reputation: 120968

The only thing that is different is the number of methods it would take to accomplish what you want. Comparator.comparingLong will apply a ToLongFunction for each argument and then delegate to Long.compare. But this is a simple optimization that JIT should take care of. I'd say that because of this the difference, there could be a small difference (until JIT kicks in), but it would be so small that is absolutely neglectable and should not drive your decision in any way.

On the other hand, if you actually see any differences, than it's probably your testing code that is the issue, not the code that is being measured.

Upvotes: 9

Related Questions