考虑以下代码:
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")
就足够了?
这对你来说可能很有趣。对于此代码:
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 次。str
split
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
,1
和2
最终检查 at 3
。
编辑
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();
}
}
输出将显示每一轮:索引调用
for loop
工作原理
int idx = 0
str.split("\\D").length;
idx++
str.split("\\D").length;
idx < str.split("\\D").length()
失败所以每次你idx < str.split("\\D").length()
都会被执行。我会说它会在条件匹配时执行三次,最后一次在条件失败时执行。
有一个 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
}
str.split("\D") 不会拆分您的 str 字符串,它会返回 String[] 数组的新实例。答案是 str.split("\D") 将执行 3 次。