Java中的for each循环解析

14

下面的代码没有达到我的预期,执行此代码后每个字符串都为空。

String[] currentState = new String[answer.length()];
for(String x : currentState)
{
    x = "_";
}

下面的代码达到了我的预期。现在currentState中的每个字符串都是"_"。

String[] currentState = new String[answer.length()];
for (int i = 0; i < currentState.length; i++) {
    currentState[i] = "_";
}

有人能解释一下为什么第一个情况不起作用吗?


你的意思可能应该是:String[] currentState = new String[answer.length()]; - Andrew Swan
6个回答

31

按照设计,foreach 循环中的变量 'x' (在这种情况下)不应该被赋值。我很惊讶它甚至可以正常编译。

String[] currentState = new String[answer.length()]; 
for (String x : currentState) { 
    x = "_"; // x is not a reference to some element of currentState 
}

以下代码可能展示了你实际在做什么。请注意,这不是枚举的工作方式,但它说明了为什么你不能分配“x”。它是位置'i'处元素的副本。(编辑:请注意,该元素是引用类型,因此它是该引用的副本,对该副本的赋值不会更新相同的内存位置,即位置“i”处的元素)

String[] currentState = new String[answer.length()]; 
for (int i = 0; i < answer.length(); i++) { 
    String x = currentState[i];
    x = "_";
}

1
哦,所以当我执行(String x:currentState)时,它会创建一个新字符串x,并复制currentState中字符串的值? - jbu
1
我很惊讶它能够顺利编译。当然它可以顺利编译。如果您不想赋值,只需将x声明为final即可。 - cadrian
1
最近我和一个人交谈,他遇到了这个问题,并正在考虑添加一个FindBugs检测器来解决它。虽然你可以这样做,但当你这样做时,出现错误的几率约为100%。就我个人而言,我认为这应该是编译器错误。 - Alex Miller
我不理解其中的合理性,为什么默认情况下它不是final?而且你为什么需要它一开始就不是final呢? - John Leidegren
1
同样的理由,除了接口数据成员之外,默认情况下没有任何东西是最终的,我猜(个人认为默认情况下应该将所有内容都设置为最终)。 - TofuBeer
显示剩余3条评论

9

原始代码:

String currentState = new String[answer.length()];

for(String x : currentState) 
{ 
    x = "_"; 
}

重写的代码:

String currentState = new String[answer.length()];

for(int i = 0; i < currentState.length; i++) 
{ 
    String x;

    x = currentState[i];
    x = "_"; 
}

我会如何编写代码:

我会怎样写代码:

String currentState = new String[answer.length()];

for(final String x : currentState) 
{ 
    x = "_";   // compiler error
}

带错误的重写代码:

String currentState = new String[answer.length()];

for(int i = 0; i < currentState.length; i++) 
{ 
    final String x;

    x = currentState[i];
    x = "_";   // compiler error
}

将变量设为final可以在你进行以下操作时突出显示(这是常见的初学者错误)。尽量让所有变量都成为final(实例、类、参数、catch中的异常等),只有当你真的需要更改它们时才将它们设为非final。你会发现,在你进行这些操作时,90%-95%的变量都应该是final的(初学者开始时可能只有20%-50%的变量是final的)。


4

因为x是一个引用(或者叫做引用类型的变量)。第一段代码所做的只是将引用指向一个新值。例如:

String y = "Jim";
String x = y;
y = "Bob";
System.out.println(x); //prints Jim
System.out.println(y); //prints Bob

你正在将引用变量 y 重新赋值为 "Bob" 并不会影响到之前被引用变量 x 所指向的内容。

在你的示例代码中,x 不是 引用。它是一个值为int的变量。与原始代码相比,其中x的值引用,因为String是引用类型。 - Jon Skeet
当然可以。我只是试图保持简单。我认为深入讨论“引用类型变量”和“基本类型变量”的语义可能是不必要的。实际上,我认为x可以被视为对某个值的引用。将类型更改为字符串。 - oxbow_lakes
如果你这样做 'y = "Bob";' 会更好... 这样更明显,当 x “指向 y” 时它不是一个指向指针的指针(一些刚开始学 Java 的人会卡在这个问题上)。 - TofuBeer

-1

for each循环用于这个:

List suits = ...;
List ranks = ...;
List sortedDeck = new ArrayList();
for (Suit suit : suits){
    for (Rank rank : ranks)
        sortedDeck.add(new Card(suit, rank));
}

所以考虑以上,你可以这样做:

String[] currentState = new String[answer.length()];
List<String> buffList = new ArrayList<>();
for (String x : currentState){
        x = "_";
        buffList.add(x);
        // buffList.add(x = "_" ); will be work too
}
currentState = buffList.toArray(currentState);

-1
你可以将数组转换为列表,然后像这样迭代:
String[] currentState = new String[answer.length()];
List<String> list = Arrays.asList(currentState);
for(String string : list) {
   x = "_";     
}

-1

Object x[]={1,"ram",30000f,35,"account"}; for(Object i:x) System.out.println(i); for each 用于顺序访问


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接