14

The code below doesn't do what I expect. Every string is null after this code executes.

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

The code below does what I expect. Every string in currentState is now "_"

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

Can someone explain why the first case doesn't work?

Sergey Glotov
  • 20,200
  • 11
  • 84
  • 98

6 Answers6

31

By design the for each variable 'x' (in this case) is not meant to be assigned to. I'm surprised that it even compiles fine.

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

The following code maybe shows what you're in effect are doing. Note that this is not how enumerations work but it exemplifies why you can't assign 'x'. It's a copy of the element at location 'i'. (Edit: note that the element is a reference type, as such it's a copy of that reference, assignment to that copy does not update the same memory location i.e. the element at location 'i')

String[] currentState = new String[answer.length()]; 
for (int i = 0; i < answer.length(); i++) { 
    String x = currentState[i];
    x = "_";
}
John Leidegren
  • 59,920
  • 20
  • 131
  • 152
  • 1
    oh, so when I do (String x : currentState), it creates a NEW string, x, and copies the VALUE of the string in currentState? –  Feb 26 '09 at 07:00
  • nevermind, I see, I'm just switching the reference –  Feb 26 '09 at 07:02
  • 1
    "I'm surprised that it even compiles fine". Of course it compiles fine. If you don't want assignment just make x final. – cadrian Feb 26 '09 at 07:03
  • It doesn't copy anything. It's just another reference to the same object. Assigning to it changes the reference, not the object (as if the object could be changed-- String is immutable). – Devin Jeanpierre Feb 26 '09 at 07:08
  • 1
    I was talking to someone recently that had run into this and was looking into adding a FindBugs detector for it. While you *can* do it, the chances that it is a bug when you do it are about 100%. Personally, I'd vote that this should be a compiler error. – Alex Miller Feb 26 '09 at 14:59
  • If you want it to be a compiler error mark the variables as final. – TofuBeer Feb 26 '09 at 20:58
  • I don't understand the rational behind that, for what reason is it not final by default? And for what reason would you ever really need it to be not final in the first place? – John Leidegren Feb 26 '09 at 21:11
  • 1
    The same rationale where nothing is final by default, save interface data members, I guess (personally I think everything should be final as default). – TofuBeer Feb 28 '09 at 04:48
9

Original code:

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

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

Rewritten code:

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

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

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

How I would write the code:

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

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

Rewritten code with the error:

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

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

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

Making the variables final highlights when you do things like this (it is a common beginner mistake). Try to make all of your variables final (instance, class, arguments, exceptions in catch. etc...) - only make them non-final if you really have to change them. You should find that 90%-95% of your variables are final (beginners will wind up with 20%-50% when they start doing this).

TofuBeer
  • 60,850
  • 18
  • 118
  • 163
4

Because x is a reference (or a variable of reference-type). All the first piece of code does is re-point the reference at a new value. For example

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

The fact that you are re-assigning the reference y to "Bob" does not affect what the reference x was assigned to.

oxbow_lakes
  • 133,303
  • 56
  • 317
  • 449
  • In your sample code x *isn't* a reference. It's a variable whose value is just an int. Compare this with the original code, where the value of x *is* a reference because String is a reference type. – Jon Skeet Feb 26 '09 at 07:03
  • Yes, of course. I was just trying to keep it simple. I thought that going in to the semantics of "variable of reference type" and "variable of primitive type" was probably unnecessary. To all intents and purposes, I think that x can be thought of as a reference to some value. Changed type to String – oxbow_lakes Feb 26 '09 at 07:12
  • Be better if you did 'y = "Bob";' ... makes it more obvious that when x "points to y" it isn't a pointer to a pointer (something some people get stuck on when the are starting with Java) – TofuBeer Feb 26 '09 at 07:17
-1

The for each loop meant for this:

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

so consider above you can do this:

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

You can convert your array to a List and then iterate like this:

String[] currentState = new String[answer.length()];
List<String> list = Arrays.asList(currentState);
for(String string : list) {
   x = "_";     
}
Luke
  • 3,381
  • 1
  • 20
  • 20
-1

Object x[]={1,"ram",30000f,35,"account"}; for(Object i:x) System.out.println(i); for each is used for sequential access