-1

I posted a reply a few days ago, and I must have been tired as I missed a break statement. However the behavior of the code puzzled me:

String desc = "Key1: Val1 ; Key2: Val2 ; Key3: Val3 ; Key4: Val4 ; Key5: Val5";
String[] parts = desc.split(";");

System.out.println("For Each -------------------------");

for ( String part : parts )
{
    System.out.println(part);

    if ( part.contains("Key3") )
    {
        parts = part.split(":");

        System.out.println("***************** Value is: '" + parts[1].trim() + "'");
    }
}

System.out.println("For Each -------------------------");

Produces

For Each -------------------------
 Key1: Val1 
 Key2: Val2 
 Key3: Val3 
***************** Value is: 'Val3'
 Key4: Val4 
 Key5: Val5
For Each -------------------------

The strange part is that I changed the contents of parts midway through the foreach loop. This should have caused an error, as the value and length of parts changes.

The loop using an index

String desc = "Key1: Val1 ; Key2: Val2 ; Key3: Val3 ; Key4: Val4 ; Key5: Val5";
String[] parts = desc.split(";");

System.out.println("Index -------------------------");

for ( int i = 0; i < parts.length; i++ )
{
    System.out.println(parts[i]);

    if ( parts[i].contains("Key3") )
    {
        parts = parts[i].split(":");

        System.out.println("***************** Value is: '" + parts[1].trim() + "'");
    }
}

System.out.println("Index -------------------------");

Produces

Index -------------------------
 Key1: Val1 
 Key2: Val2 
 Key3: Val3 
***************** Value is: 'Val3'
Index -------------------------

The loop ends because the length of parts is changed, and the for test exits the loop. We never see Key4 and Key5.

The question is, why does the foreach loop continue, and with what values?

This was run using Eclipse and a jpage file.

Big Guy
  • 332
  • 1
  • 13
  • Does this answer your question? [How does the Java 'for each' loop work?](https://stackoverflow.com/questions/85190/how-does-the-java-for-each-loop-work) – Charlie Armstrong Mar 31 '21 at 21:13
  • Not really, as part of the answer in the referal states ```Also, if the right-hand side of the for (:) idiom is an array rather than an Iterable object, the internal code uses an int index counter and checks against array.length instead.``` which would mean that the ```foreach``` should have exited early – Big Guy Mar 31 '21 at 21:17
  • In short, the foreach loop creates an iterator, and that iterator retains a reference to the original array. When you re-assign the `parts` variable, all that means is that all subsequent access to the `parts` variable will now be accessing the new array. It does **not** mean that the original array went away, the iterator **still** has that reference. But your standard for loop didn't store a reference to the array, it's still reading from the `parts` variable on every iteration. – Charlie Armstrong Mar 31 '21 at 21:19
  • You might be right on the duplicate though, I'll admit it's a bit of a stretch. I've retracted the duplicate flag. – Charlie Armstrong Mar 31 '21 at 21:21
  • @CharlieArmstrong I understand the basic for loop, and why it exited early. But when I reassigned ```parts```, what it references changed. The ```foreach``` is now iterating over something that for all intents and purposes no longer exists. – Big Guy Mar 31 '21 at 21:24
  • I've added an answer, hopefully that makes it a little clearer. The original array does still exist; you just can't see it because it's been hidden under the hood of the foreach loop. – Charlie Armstrong Mar 31 '21 at 21:58

1 Answers1

1

We can take a look at the Java Language Specification (JLS) Section 14.14.2 to get an idea of how foreach loops (also called enhanced for statements in the JLS) work under the hood:

The enhanced for statement has the form:

EnhancedForStatement:
  for ( {VariableModifier} LocalVariableType VariableDeclaratorId : Expression ) Statement

EnhancedForStatementNoShortIf:
  for ( {VariableModifier} LocalVariableType VariableDeclaratorId : Expression ) StatementNoShortIf

...

  • If the type of Expression is a subtype of Iterable, then the translation is as follows.

Well, this is great if we are iterating over an iterator, but in your case, you are iterating over an array, so I will skip that section and go to:

  • Otherwise, the Expression necessarily has an array type, T[].

    Let L1 ... Lm be the (possibly empty) sequence of labels immediately preceding the enhanced for statement.

    The enhanced for statement is equivalent to a basic for statement of the form:

    T[] #a = Expression;
    L1: L2: ... Lm:
    for (int #i = 0; #i < #a.length; #i++) {
        {VariableModifier} TargetType Identifier = #a[#i];
        Statement
    }
    

The answer here is in the first line of that equivalent basic for loop. The array that was passed to the foreach loop, parts, is called Expression here. In the first line, the JLS is saying the foreach loop stores a reference to that array in a variable called #a. This is a lot of theoretical stuff, so let's bring it back to your scenario by turning your foreach loop into the equivalent standard for loop. Your foreach loop is essentially:

for (String part : parts) {
    ...
}

When we expand this out to look like the for loop from the JLS, we get this:

String[] a = parts;

for (int i = 0; i < a.length; i++) {
    String part = a[i];
    ...
}

The key here is that this for loop is not actually iterating over parts. It's iterating over a, and a was set to whatever array parts was referencing at the time of the assignment, which was before the for loop. After that, you can assign a new array to parts, but that doesn't change the fact that a is still assigned to the original array. A short snippet of code demonstrating this:

String str1 = "hello";
String str2 = str1;
str1 = "goodbye";
System.out.println(str2);

Will print out:

hello

Because str2 retained a reference to the "hello" object. The same thing happens in your for loop with the arrays. a retains a reference to the original array object, even when you change what parts refers to.

Charlie Armstrong
  • 2,332
  • 3
  • 13
  • 25
  • 1
    Ok, I see, the ```foreach``` creates its own private reference to the original array. I kind of suspected something like that. It also means that the ```foreach``` does not go past the bounds of the changed array, as it has its own reference to the old array. Thanks – Big Guy Mar 31 '21 at 22:04