0

Consider the following piece of code:

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

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

How many times will this portion of code: str.split("\\D") be executed? Three times? Or will the compiler see that as str is declared as final, only one call to str.split("\\D") will be enough?

1
  • what is the problem u r facing in it ??Post u r problem Commented Sep 5, 2013 at 8:24

6 Answers 6

6

This might be interesting to you. For this code:

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

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

the bytecode is:

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        
}

It is clear that split is executed three FOUR times, regardless of the finality of str, and of invariance of split.

Sign up to request clarification or add additional context in comments.

3 Comments

Good answer (especially the bytecode), the only problem is that it's executed four times since it does so for the final "false" condition as well. Change that and it'll be perfect :-)
That's better, +1 :-)
@paxdiablo: Way ahead of you :) It said "four" for like 20 minutes :D
3

JLS 14.14.1.2 states (Expression here being the middle bit of the for statement):

Next, a for iteration step is performed, as follows. If the Expression is present, it is evaluated.

That means it does it on each iteration step, there is no leeway there in the specification for optimisation.

You can see something similar with the following code:

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);
        }
    }
}

When you run that, you get:

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()

showing that the function is called each time through the loop (including the exit condition).

That means your particular case won't call it once or three times, it will actually call it four times, with idx set respectively to 0, 1, 2 and the final check at 3.

2 Comments

200% agree but wont it get execute to check for failure condition?.
@captain: Yes. It "gets seven" eight times.
1

How does for loop works

  1. Initialization ---> int idx = 0
  2. Condition check ---> str.split("\\D").length;
  3. Execute Loop body
  4. Increment index idx++
  5. check condition str.split("\\D").length;
  6. Execute Loop body
  7. Repeat steps 4-6 till idx < str.split("\\D").length() fails

So every time your idx < str.split("\\D").length() will get executed. I would say it will get execute three times for condition match and one last for condition fail.

Comments

1

There's a 100% sure way to see if a compiler optimizes something - look at the compilation result. I've included the byte code in the response and I think it's pretty obvious - the split method will be executed multiple times in the first case regardless of the final keyword (the invoke virtual line is the split method call, you can understand what is looped by the goto statement).

Different compilers MAY behave in a different way, though. Feel free to retest this on your desired environment (you can view bytecode with '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
        }
    }
}

results in:

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
}

while

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
        }
    }
}

results in:

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
}

1 Comment

You're right about the 100% sure way but I have to disagree with you that some compilers may do something different. The JLS requires the expression to be evaluated, any compiler that doesn't do so is not a Java compiler.
1

EDIT

The split method will be executed 3 times to determine the length, BUT this has nothing to do with the fact that the string is final or not. It would be execute the same even if the strign was not final.

See this example:

/* 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();
    }
}

The output will show for each round: index Called

7 Comments

Thanks. What will happen if I don't declare the string as final?
The execution will not be different depending on the string if it is final or not.
JLS 14.14.1.2 states that the expression is evaluated for each iteration. That doesn't seem to gel with what you're saying here.
@CloudyMarble: How is the source code of split relevant to knowing how many times it is being called (from outside itself)?
@paxdiablo the JLS 14.14.1.2 refer to the for statetment, the question refers to how many times the statement str.split("\\D") is beeing executed.
|
0

str.split("\D") will not split your str string it returns new instance of String[] array. Answer is 3 times str.split("\D") will be executed.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.