【问题标题】:Is it better to store values from instance fields as variables prior to using them in a method call, or call the get() methods within the method call?在方法调用中使用它们之前将实例字段中的值存储为变量,还是在方法调用中调用 get() 方法更好?
【发布时间】:2021-11-18 21:17:26
【问题描述】:

我想知道执行以下操作是否更好:

String currentUserName = user.getName();
Integer currentUserAge = user.getAge();

User foundUser = userRepository.findByAgeAndUsername(currentUserName, currentUserAge);

User foundUser = userRepository.findByAgeAndUsername(user.getName(), user.getAge());

其中一个是否比另一个更高效?我想做第一个,因为它提高了代码库的可读性和可理解性,但它看起来确实很冗长,并且在 IDE 上占用了大量的文字空间(虽然可能不是内存空间?)。我正在寻找这些方法的任何优点或缺点,而不仅仅是在这个特定示例中。

谢谢

【问题讨论】:

  • 没有性能差异。如果仅此而已,它们也应该编译为相同的字节码。
  • 这两者的区别是零。这不是一个微小的差异,而是一个的差异。
  • @LouisWasserman 但是编译器不会花时间优化这些变量吗?让它们实际上只是 cmets?
  • 并非如此。它编译成同样的东西。在编译时,我想它可能会节省几纳秒。你花在考虑它上的时间,以最低工资为预算,比编译时节省的 CPU 量更昂贵。
  • 如果我们假设 getName() 和 getAge() 是简单的 getter,那根本没有区别。但是,如果它们包含复杂的计算或一些繁重的 I/O(这是不好的做法,但谁没有见过 getter 执行的操作不仅仅是返回值)?

标签: java spring performance qa software-quality


【解决方案1】:

始终使您的代码尽可能易读易懂。您应该假设其他人在某些时候需要调试、修改、扩展或使用您的代码。在某种程度上,是什么使代码或多或少具有可读性取决于个人喜好,但在这种情况下我同意你的观点,即第一个选项更简单。

它在 IDE 中占用更多空间这一事实并不是什么大问题。一些项目有编码指南,例如“一个方法不应超过 X 行”。这个想法是能够在不滚动太多的情况下阅读整个方法。为了满足这样的指导方针,有时程序员会尽可能多地塞进一行,这违背了指导方针的可读性目标,所以不要那样做。如果一个方法变得太长,这很好地表明它做得太多了,因此请寻找一种方法将其拆分为合理的部分。

关于性能,我总是提醒人们解决他们遇到的问题!如果您有性能问题,那么,很好,寻找解决它的方法。如果您没有性能问题,请不要浪费时间尝试修复它!你有足够的问题要解决,对吧?此外,编译器和解释器真的非常擅长寻找提高性能的方法。让那些人担心这些东西。

【讨论】:

    【解决方案2】:

    理论上,它不应该对性能产生任何影响,因为 JIT 应该能够优化这些琐碎的事情。

    但是,有一个警告;内联的阈值取决于字节码指令的大小,而不是硬件指令的大小/数量。因此,如果您使方法更大,它可能会阻止它被内联。内联是许多其他优化的推动力。

    例子:

    public class Foo{
        int sum(int a,int b, int c){
            return a+b+c;
        }
        
        int long_method(Record record){
           int a = record.getA();
           int b = record.getB();
           int c = record.getC();
           return sum(a,b,c);
        }
        
        int short_method(Record record){
           return sum(record.getA(),record.getB(),record.getC());
        }
    }
    
    class Record{
       int a;
       int b;
       int c;
       
       int getA(){return a;}
       int getB(){return b;}
       int getC(){return c;}
    }
    

    编译后查看字节码:

    javap -c Foo.class 
    Compiled from "Foo.java"
    public class Foo {
      public Foo();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."<init>":()V
           4: return
    
      int sum(int, int, int);
        Code:
           0: iload_1
           1: iload_2
           2: iadd
           3: iload_3
           4: iadd
           5: ireturn
    
      int long_method(Record);
        Code:
           0: aload_1
           1: invokevirtual #2                  // Method Record.getA:()I
           4: istore_2
           5: aload_1
           6: invokevirtual #3                  // Method Record.getB:()I
           9: istore_3
          10: aload_1
          11: invokevirtual #4                  // Method Record.getC:()I
          14: istore        4
          16: aload_0
          17: iload_2
          18: iload_3
          19: iload         4
          21: invokevirtual #5                  // Method sum:(III)I
          24: ireturn
    
      int short_method(Record);
        Code:
           0: aload_0
           1: aload_1
           2: invokevirtual #2                  // Method Record.getA:()I
           5: aload_1
           6: invokevirtual #3                  // Method Record.getB:()I
           9: aload_1
          10: invokevirtual #4                  // Method Record.getC:()I
          13: invokevirtual #5                  // Method sum:(III)I
          16: ireturn
    }
    

    很明显long_method的字节码大小比short_method的字节码大小要大。

    在大多数情况下,代码的清晰度应该优先于优化;当然,如果没有证据表明未优化的代码是一个瓶颈。

    有关更多信息,请参阅以下post,其中还包括一些基准测试。

    【讨论】:

      【解决方案3】:

      在性能方面确实没有区别。在你给出的例子中,它真的没有什么区别。

      -- 但是--

      这里有两个重要方面:

      1. 尝试将信息存储在 1 个位置。当您将信息存储在两个位置时,信息可能会不同步。一个地方可以说一件事,另一个地方可以说另一件事。在这种情况下几乎肯定不会有什么影响,但作为一种编程习惯,如果您可以从源中检索,最好不要存储在临时变量中(尽管有时有理由存储)

      2. 代码清晰性:通过创建变量userNameuserAge 变量,它可以让你在做什么更清晰(更详细一点)。 (你也可以使用 cmets 来做同样的事情)

      【讨论】:

      • 第三个重要的方面:将所有代码塞进一行,使得在调试器下运行时更难进入这些函数。
      • 我不同意你的第二点。 userName 并不比user.getName 更清楚,它们是相同的......但是如果你创建一个变量,读者应该明白这是有原因的,但没有。因此,创建中间变量会使代码意图更难理解……因为您必须意识到该变量没有任何用途。
      猜你喜欢
      • 2011-03-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-03
      • 1970-01-01
      • 2011-04-22
      • 1970-01-01
      相关资源
      最近更新 更多