【问题标题】:Java - Declaring variables in for loopsJava - 在 for 循环中声明变量
【发布时间】:2011-05-28 22:32:46
【问题描述】:

在循环内声明变量是不好的做法吗?在我看来,这样做,如下面的第一个代码块所示,将使用十倍于第二个代码块的内存......由于在循环的每次迭代中创建一个新字符串。这是正确的吗?

for (int i = 0; i < 10; i++) {
  String str = "Some string";
}

对比

String str;
for (int i = 0; i < 10; i++) {
  str = "Some String";
}

【问题讨论】:

  • String 是不可变类,总是在写入时创建新实例。但是您的情况,由于常量值,编译器可能会对其进行优化。
  • 也许字符串对于这个例子来说是一个糟糕的选择。我真正的问题是......如果在循环中声明了一个变量,是否为每次迭代声明了一个新变量,或者编译器是否将其优化为单个变量?
  • 变量声明和对象构造是两个不同的东西。如果您在执行 N 次的循环内执行 final Foo foo = new Foo(someArg);,它将构造 N 个单独的对象,而不是在执行一次的循环之外。但是如果你在循环外有final Foo foo1 = new Foo(someArg);,然后在循环内有final Foo foo = foo1,你只会实例化1个对象。字符串有点特殊,因为它们是不可变的常量,编译器可能会将其优化为创建一个 String 对象,并在每次循环中重复使用它。

标签: java micro-optimization


【解决方案1】:

在循环内声明变量是不好的做法吗?

一点也不!它将变量本地化到其使用点。

在我看来,如下面的第一个代码块所示,这样做会使用十倍于第二个代码块的内存

编译器可能会优化一些东西以保持内存使用效率。仅供参考:如果您使用 final 关键字告诉它您的变量具有对对象的固定引用,您可以帮助它。

注意:如果您有一个更复杂的对象,您在构造函数中执行复杂代码,那么您可能需要担心单次执行与多次执行,并在循环之外声明该对象。

【讨论】:

  • 只是一个后续.. 如果我们有 String str = new String("Some String");在循环?我的理解是它会创建 10 个实例!
  • @Ravi:你可能是对的,因为你明确地调用了 String 构造函数,我相当肯定它绕过了重复使用常量 String 实例的字符串实习操作。
  • 但即使在这种情况下,如果将 str 的声明移到循环外,并将赋值留在循环内,您仍然会创建相同的 10 个实例。
  • @ILMTitan:是的,你是对的。我只是想知道即使有一个新的运算符,JVM 是否也会优化。
  • 它会创建十个实例,是的,但是您必须这样做超过九千次才能获得任何显着的性能影响。将变量保持在范围内允许运行时快速释放该变量(并在迭代中重新使用它) - 将变量移动到周围的范围将使其在内存中的保留时间更长。无论如何,您仍然会为每次迭代重新创建对象实例。
【解决方案2】:

在这两个示例中,您将实例化一个新的字符串对象,该对象包含相同次数的字符串“Some String”。

在第一个示例中,您在循环内声明 str,在 for 循环完成后,对该字符串的所有引用都将丢失,从而允许 Java 的垃圾收集器从内存中删除所有字符串实例。但是,在第二个示例中,您在循环外声明 str,您创建的最后一个字符串仍将在循环外引用它,并且 Java 的垃圾收集器只会从内存中删除 10 个字符串中的 9 个被实例化了。

因此,第一种方法更好,因为您不保留字符串的任何引用,这会干扰垃圾收集器确定它是否仍在使用中的能力。

【讨论】:

    【解决方案3】:

    除了@Jason S 所说的之外,我还鼓励您考虑代码的可读性。

    例如,如果您只写一次引用,那么使用以下内容会使您的意图更加清晰:

    String str = "write once";
    while(condition){
        //do stuff with str
    }
    

    对比:

    String str = null;
    while(condition){
        str = "write once";
        //do stuff with str
    }
    

    同样,如果字符串的值真的基于特定于循环的迭代,则在循环内声明变量。

    【讨论】:

      【解决方案4】:

      变量引用使用的内存很小。在循环中声明项目通常是更好的做法,因为它更接近使用它的位置并且更具可读性。

      【讨论】:

      • 变量引用占用的内存确实很小。但是,如果循环从 0 迭代到 10000000000,并且使用代码块 #2 只使用一个变量引用(而不是块 #1 的每次迭代一个变量引用),您肯定会看到这将是一个问题。
      • 是的,即使你循环了很多次,这也不会有太大的不同。正如其他人所说,编译器甚至可能对其进行优化,您可以对其进行测试并确定。但无论哪种方式,使代码更难阅读的代价都不值得非常小的性能改进。
      【解决方案5】:

      这取决于。效率低下是由于创建 String 对象的开销,假设您的编译器没有更改任何内容。一旦超出范围,内存将被清除。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-17
        • 2015-03-25
        • 1970-01-01
        • 2021-08-29
        相关资源
        最近更新 更多