Simplify a loop [closed]

Posted on

Problem

What would your preferred loop type be for a case like this:

StringBuilder sb = new StringBuilder();
for(Integer id : idList) {
    sb.append("?,");
}

In short: In dependency to the size of a List i want to add stuff to a String. The above example produces a “unused variable” warning.

I’m thinking of these alternatives:

for(int i=0; i<idList.size(); i++) {
    // ...
}

I don’t really like all the extra typing there…

Iterator it = idList.iterator();
while(it.hasNext()) {
    // ...
    it.next();
}

The loop head itself looks nice, but i end up typing a lot of extra code (getting the iterator, moving to the next element…)

What I’d like to do would be something like that:

for(idList) {
    // ...
}

Is there a better style/What is the best kind of style for that kind of loop?

Solution

I would use a “normal” string repeating method:

String s = repeat("?,", idList.size());

Unfortunately Java doesn’t have such a method in the standard API. So you could write one yourself:

String repeat(String s, count i) {
  StringBuilder sb = new StringBuilder();
  while (i-- > 0) {
     ab.append(s);
  }
  return sb.toString();
}

or use one provided by several “standard add-on APIs” such as Apache Commons’ StringUtils.repeat().

The second example is by far the clearest in expressing your intention. You want to do something an amount of times, in this case, idList.size().

for( int i = 0; i < idList.size(); ++i ) {
    // ...
}

In the third one you are basically just writing what the first example already generates, so that’s definitely not an improvement.

You do have a point that a for loop is rather a big code construct to express just executing a piece of code an amount of times. Ruby’s times operator is made exactly for this purpose. In other languages there are some code constructs available which could help you. Extension methods in C# allow you to create a similar operator as the times operator of Ruby.

For Java I don’t believe a more concise syntax is possible since closures aren’t supported in Java by default.


As in RoToRa’s answer, a simple String helper class containing a Repeat function is most likely the cleanest solution for you. It doesn’t provide the functionality to repeat any type or operation, but it does provide the concise syntax you are looking for.

I suggest to use Strings.repeat() from Google Guava library:

String result = Strings.repeat("?,", idList.size());

I would go with the first alternative and use @SuppressWarnings.

If you need this pattern more often, how about an object oriented solution:

public static class Repeat<T> implements Iterable<T> {
    private final T[] ts;
    private final int n;

    public Repeat(int n, T ... ts) {
        this.ts = ts;
        this.n = n;
    }

    public Iterator<T> iterator() {
        return new Iterator<T>() {
            int count = 0;   

            public boolean hasNext() {
                return count < n;
            }

            public T next() {
                if (! hasNext()) {
                    throw new NoSuchElementException();
                }
                return ts[(count++) % ts.length];
            }

            public void remove() {
                throw new UnsupportedOperationException("Not supported yet.");
            }

        };
    }
    public static <U> Repeat<U> repeat(int n, U ... us) {
        return new Repeat<U>(n, us);
    }
}

//usage
import static blabla.Repeat.*;
...
for(String s : repeat(idList.size(), "?,")) {
   sb.append(s);
}

I’d personally use the second version, i.e. the for loop because it is clearer. The first example looks like you’ve introduced an error by not using the id, the third adds two extra lines of code, but isn’t too bad on the eyes.

I think I would also create a local cache of the size() so that you can avoid the function call on every iteration of the loop. But this is a compromise between speed, the size of the list, readability and whether the list size will change during the loop.

int size = idList.size();
for (int i = 0; i < size; ++i) {...}

If this is a heavily called inner loop the saving yourself a few tens of thousands of function calls to return back a “constant” can save some time. But if you do something like this it might be worth a comment to say why you did it “differently” to an normal expected way.

I like for loops because they are very self contained; you get start value, end condition and increment operation all on one line whereas a while loop requires hunting for the “++” somewhere within the while block itself.

I think keeping your intention simple and clearly seen is more important going forward when your code has to be maintained. Remember to code as if the person maintaining your code is serial axe murderer who know where you live!

Leave a Reply

Your email address will not be published. Required fields are marked *