Reputation: 7546
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
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
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
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
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