Refactor foreach to for loop

8.9k views Asked by At

I have a foreach loop in Java (simplified version here)

List<String> names = getNames();
for(String name:names) {
    doSomething(name);
}

Is there an automated way to refactor this to a traditional for loop?

I know how to do it manually

List<String> names = getNames();
for(int i=0; i<names.size(); i++) {
    String name = names.get(i);
    doSomething(name);
}

As you can see, there is a bit of typing needed in the for statement itself as well as introducing the variable name again and assign it the value names.get(i). In total, the manual edit is too error-prone for me.

Why do I want to do this? I have to fix a bug and the fix is to start at index 1 instead of index 0 and end at index n-1 instead of the end (unfortunately I can't fix the input right away, I need to wait for a library update if that's recognized as a bug).

What have I tried? I right-clicked on the for keyword and clicked on "Refactor", but as far as I can get from the context menu entries, nothing in there would do the work for me.

Why do I think this could theoretically work? Because similar functionality exists in Resharper for Visual Studio (C#).

FYI: I'm using Eclipse Luna SR 2 (4.4.2)

8

There are 8 answers

2
Massimo On BEST ANSWER

Mouseover the for statement, right-click, Quick fix (Ctrl+1), convert to indexed loop.

Should work!

1
Bathsheba On

Be careful with this refactoring.

The reason is that for a traditional linked list, your second for loop formulation is O(N * N) since you're having to traverse the linked list in order to evaluate names.get(i);. That could become expensive.

Do consider performance implications when moving from for(String name:names) {. There may well be a better way of fixing your immediate bug, and retaining the current "Big O".

for(String name : names.subList(1, names.size() - 1)) {

is one such way (Acknowledge @JB Nizet).

0
Marvin On

In my eclipse (Kepler RC2) it works to select the for keyword and either use the quick fix from the context menu or hit CTRL+1 for the shortcut. Eclipse then offers me "Convert to indexed 'for' loop" or "Convert to Iterator-based 'for' loop".

Screenshot of quick fix options

2
JB Nizet On
List<String> names = getNames();
names = names.subList(1, names.size() - 1);
for(String name : names) {
    doSomething(name);
}

Of course, you could put that into a reusable method if you need to do it several times:

public static List<String> fixList(List<String> names) {
    return names.subList(1, names.size() - 1);
}

and then use it as

List<String> names = fixList(getNames());
for(String name : names) {
    doSomething(name);
}
0
grojas123 On

In my case, I need to transform the foreach to use an index . This is my two cents

Integer index=0;
                  
      for (final String OneCell :CellList){
     // Your code use the index
             index=index+1;
     }
2
Stefaan Neyts On

When using java 8 you could use the stream api

names.stream().skip(1).reverse().skip(1).reverse().foreach(
    name -> do something(name)
);

Something like this...

0
Estimate On

You can use either:

names = names.subList(1, names.size()-1);
for (String name : names) {
   doSomething(name);
}

or in manually:

for (int i = 1; i < names.size()-1; i++) {
   String name = names.get(i);
    doSomething(name);
}

But the first one I prefer to use.

0
JnRouvignac On

Don't go for indexed iteration! This does not perform well with all implementations of List.

It is much better to go for an Iterator and let the JIT optimize the Iterator away if this code is hot.

So either write this:

List<String> names = getNames();
for (String name : names.subList(1, names.size() - 1)) {
    doSomething(name);
}

Or that (one allocation less):

Iterator<String> it = getNames().iterator();
it.next(); // You seem to be sure there is more than one element in the list
while (it.hasNext()) {
    String name = it.next();
    doSomething(name);
}