0

考虑以下代码:

final String str = "1-2-3";

for (int idx = 0; idx < str.split("\\D").length; idx++) {
    // do something
}

这部分代码:str.split("\\D")将被执行多少次?三次?还是编译器会看到 asstr声明为final,只调用一次str.split("\\D")就足够了?

4

6 回答 6

6

这对你来说可能很有趣。对于此代码:

class Foo {
  public static void main(String args[]) {
    final String str = "1-2-3";

    for (int idx = 0; idx < str.split("\\D").length; idx++) {
    }
  }
}

字节码是:

Compiled from "Foo.java"
class Foo {
  Foo();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return        

  public static void main(java.lang.String[]);
    Code:
       0: iconst_0      
       1: istore_2      
       2: iload_2       
       3: ldc           #2                  // String 1-2-3
       5: ldc           #3                  // String \D
       7: invokevirtual #4                  // Method java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String;
      10: arraylength   
      11: if_icmpge     20
      14: iinc          2, 1
      17: goto          2
      20: return        
}

很明显,无论 的最终性和 的不变性如何,split都执行了 3次4 次。strsplit

于 2013-09-05T08:34:20.457 回答
3

JLS 14.14.1.2 状态(Expression这里是for语句的中间部分):

接下来,执行 for 迭代步骤,如下所示。如果 Expression 存在,则对其进行评估。

这意味着它在每个迭代步骤中都会这样做,规范中没有优化的余地。

您可以看到与以下代码类似的内容:

class Test {
    public static int getSeven () {
        System.out.println ("called getSeven()");
        return 7;
    }
    public static void main (String[] args){
        for (int i = 1; i <= getSeven(); i++) {
            System.out.println ("i = " + i);
        }
    }
}

当你运行它时,你会得到:

called getSeven()
i = 1
called getSeven()
i = 2
called getSeven()
i = 3
called getSeven()
i = 4
called getSeven()
i = 5
called getSeven()
i = 6
called getSeven()
i = 7
called getSeven()

表明每次通过循环调用该函数(包括退出条件)。

这意味着您的特定情况不会调用它一次三次,它实际上会调用它四次idx分别设置为0,12最终检查 at 3

于 2013-09-05T08:34:38.423 回答
1

编辑

split 方法将执行 3 次以确定长度,但这与字符串是否为最终字符串无关。即使 strign 不是最终的,它也会执行相同的操作。

看这个例子

/* package whatever; // don't place package name! */

import java.util.*;
import java.lang.*;
import java.io.*;

/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
    public static void main (String[] args) throws java.lang.Exception
    {
        String g = "bla bla";
        for (int i = 0; i < index(g); i++)
        {
            System.out.println("Round");
        }
    }

    public static int index(String input)
    {
        System.out.println("index Called");
        return input.length();
    }
}

输出将显示每一轮:索引调用

于 2013-09-05T08:23:17.350 回答
1

for loop工作原理

  1. 初始化--->int idx = 0
  2. 条件检查--->str.split("\\D").length;
  3. 执行循环体
  4. 增量索引idx++
  5. 检查条件 str.split("\\D").length;
  6. 执行循环体
  7. 重复步骤 4-6 直到idx < str.split("\\D").length()失败

所以每次你idx < str.split("\\D").length()都会被执行。我会说它会在条件匹配时执行三次,最后一次在条件失败时执行。

于 2013-09-05T08:31:16.050 回答
1

有一个 100% 确定的方法可以查看编译器是否优化了某些东西 - 查看编译结果。我已经在响应中包含了字节码,我认为这很明显——split方法在第一种情况下会被执行多次,不管final关键字是什么(invoke虚行是split方法调用,你可以理解什么是由 goto 语句循环)。

但是,不同的编译器可能会以不同的方式表现。随意在您想要的环境中重新测试它(您可以使用 'javap -c classname' 查看字节码

public class gti {
    public static void main ( String[] args ) {
        final String str = "1-2-3";

        for (int idx = 0; idx < str.split("\\D").length; idx++) {
            // do something
        }
    }
}

结果是:

public class gti {
  public gti();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_0
       1: istore_2
       2: iload_2
       3: ldc           #2                  // String 1-2-3
       5: ldc           #3                  // String \D
       7: invokevirtual #4                  // Method java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String;
      10: arraylength
      11: if_icmpge     20
      14: iinc          2, 1
      17: goto          2
      20: return
}

尽管

public class gti {
    public static void main ( String[] args ) {
        final String str = "1-2-3";
        int length = str.split("\\D").length;
        for (int idx = 0; idx < length; idx++) {
            // do something
        }
    }
}

结果是:

public class gti {
  public gti();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String 1-2-3
       2: ldc           #3                  // String \D
       4: invokevirtual #4                  // Method java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String;
       7: arraylength
       8: istore_2
       9: iconst_0
      10: istore_3
      11: iload_3
      12: iload_2
      13: if_icmpge     22
      16: iinc          3, 1
      19: goto          11
      22: return
}
于 2013-09-05T08:38:28.337 回答
0

str.split("\D") 不会拆分您的 str 字符串,它会返回 String[] 数组的新实例。答案是 str.split("\D") 将执行 3 次。

于 2013-09-05T08:28:35.240 回答