【问题标题】:Using Comparator.comparingLong has any advantage over long.compare?使用 Comparator.comparingLong 比 long.compare 有什么优势?
【发布时间】:2018-04-06 01:28:59
【问题描述】:

出于好奇,如果在 java8 样式中使用 Comparator,即使用 Lambda 表达式,是否比常规比较具有任何优势,即

按id排序的一种方法是:-

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

其他方法可能是 Java 8 方式:-

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

后一种方法 (java-8 method reference) 与前一种方法相比有什么性能优势吗?

请帮忙!!!

【问题讨论】:

  • 任何差异都可以忽略不计。他们都做同样的事情。
  • 在 'java.util.Comparator.comparingLong' 方法中,您可以看到与第一个示例 '(c1, c2) -> Long.compare(keyExtractor.applyAsLong(c1), keyExtractor. applyAsLong(c2))'
  • 工程师喜欢测量事物。您能否尝试通过执行 100K 次来衡量性能?很高兴在答案部分看到您的发现。
  • 单个比较之间的任何性能差异都无关紧要,最大的性能损失是由实际排序造成的。如果您试图根据性能选择一个而不是另一个,那么您并不真正了解性能调整(或排序)。
  • 第二种变体的好处不是性能,而是简化。它更短,并且避免了代码重复。这不仅仅是一个理论点。即使在如此短的代码 sn-ps 中,我也看到过复制和粘贴错误,例如(a, b) -> Long.compare(a.getId(), a.getId()) 您必须仔细查看才能发现错误。如果您希望这个特定的 sn-p 更快,请使用 List<AC> sortedAcs=new ArrayList<>(ac); sortedAcs.sort(Comparator.comparingLong(AC::getId)); 就地排序。

标签: java java-8


【解决方案1】:

唯一不同的是完成所需的方法的数量。 Comparator.comparingLong 将为每个参数应用一个ToLongFunction,然后然后 委托给Long.compare。但这是JIT 应该注意的简单优化。我想说,由于这种差异,可能会有一个小的差异(直到 JIT 开始),但它会非常小,绝对可以忽略不计,不应该以任何方式推动你的决定。

另一方面,如果您确实看到任何差异,那么问题可能是您的测试代码,而不是被测量的代码。

【讨论】:

  • 所以基本上会有性能影响,即使在 lambda 表达式的情况下它是轻微的。
  • @VinayPrajapati 两个示例都使用 lambdas。另一个只是使用方法引用。
  • 是的!我的错。我的意思是使用方法参考
  • @VinayPrajapati lambda 和方法引用是有区别的,但是否有任何显着影响值得怀疑。见softwareengineering.stackexchange.com/questions/277473/…
【解决方案2】:

两个代码 sn-ps 的任何性能差异都可以忽略不计。如果你真的需要优化那个代码,那么不使用流可能会比替换那个比较器给你带来更大的性能提升。

在这两种变体之间进行选择的唯一标准是清晰度:您认为哪一种更清楚地传达了代码的意图?最后,这是个人偏好,具体取决于您对 Java 8 功能的熟练程度等。

就个人而言,我发现第二个 sn-p 比第一个更清晰。 comparingLong 方法(和其他 comparingX 方法)立即告诉我:这里我们根据 (long-typed) 属性的值比较对象。在第一个 sn-p 中,我首先需要解析代码以确定确实发生了什么。

【讨论】:

    【解决方案3】:

    所以,这里从一个角度给出答案,即性能。

    这是我用来测试它的代码:-

    AC类:-

    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 +
                    '}';
        }
    }
    

    主类:-

    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();
            }
        }
    }
    

    使用 JMH 将下面的输出用于使用 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
    

    对于下面的Long.compare:-

    # Run complete. Total time: 00:00:40
    
    Benchmark          Mode  Cnt        Score        Error  Units
    Main.measureName  thrpt   20  4106542.318 ± 146956.814  ops/s
    

    如果我按照这些统计数据,Long.compare 会更快,尽管差异很小。

    如果有任何发现,请随时将您的发现放入 cmets,我也会尝试。

    【讨论】:

    • 如果我计算一下,一般来说几乎是 24ms 的差异。但看起来Long.compare 的不确定性更大。
    • 这些结果表明 comparingLong() 稍微快一点——它具有更高的吞吐量,但只有大约 0.6%。但是,comparingLong() 更具可读性,并且由于它具有相同或更好的性能,因此使用它是明确的选择。
    【解决方案4】:

    在收到有关使用的警告之前,我试图对地图进行排序

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

    应该使用 Comparator.comparingLong 替换它,结果确实是实现它的更好方法:

    Comparator.comparingLong(Map.Entry::getKey)
    

    查看compareLong的实现,我发现它的实现基本相同,只是它更干净,耗时更少。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-04
      • 2017-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多