Simple way to repeat a string

Viewed 723694

I'm looking for a simple commons method or operator that allows me to repeat some string n times. I know I could write this using a for loop, but I wish to avoid for loops whenever necessary and a simple direct method should exist somewhere.

String str = "abc";
String repeated = str.repeat(3);

repeated.equals("abcabcabc");

Related to:

repeat string javascript Create NSString by repeating another string a given number of times

Edited

I try to avoid for loops when they are not completely necessary because:

  1. They add to the number of lines of code even if they are tucked away in another function.

  2. Someone reading my code has to figure out what I am doing in that for loop. Even if it is commented and has meaningful variables names, they still have to make sure it is not doing anything "clever".

  3. Programmers love to put clever things in for loops, even if I write it to "only do what it is intended to do", that does not preclude someone coming along and adding some additional clever "fix".

  4. They are very often easy to get wrong. For loops involving indexes tend to generate off by one bugs.

  5. For loops often reuse the same variables, increasing the chance of really hard to find scoping bugs.

  6. For loops increase the number of places a bug hunter has to look.

32 Answers

Here is the shortest version (Java 1.5+ required):

repeated = new String(new char[n]).replace("\0", s);

Where n is the number of times you want to repeat the string and s is the string to repeat.

No imports or libraries needed.

So you want to avoid loops?

Here you have it:

public static String repeat(String s, int times) {
    if (times <= 0) return "";
    else return s + repeat(s, times-1);
}

(of course I know this is ugly and inefficient, but it doesn't have loops :-p)

You want it simpler and prettier? use jython:

s * 3

Edit: let's optimize it a little bit :-D

public static String repeat(String s, int times) {
   if (times <= 0) return "";
   else if (times % 2 == 0) return repeat(s+s, times/2);
   else return s + repeat(s+s, times/2);
}

Edit2: I've done a quick and dirty benchmark for the 4 main alternatives, but I don't have time to run it several times to get the means and plot the times for several inputs... So here's the code if anybody wants to try it:

public class Repeat {
    public static void main(String[] args)  {
        int n = Integer.parseInt(args[0]);
        String s = args[1];
        int l = s.length();
        long start, end;

        start = System.currentTimeMillis();
        for (int i = 0; i < n; i++) {
            if(repeatLog2(s,i).length()!=i*l) throw new RuntimeException();
        }
        end = System.currentTimeMillis();
        System.out.println("RecLog2Concat: " + (end-start) + "ms");

        start = System.currentTimeMillis();
        for (int i = 0; i < n; i++) {
            if(repeatR(s,i).length()!=i*l) throw new RuntimeException();
        }               
        end = System.currentTimeMillis();
        System.out.println("RecLinConcat: " + (end-start) + "ms");

        start = System.currentTimeMillis();
        for (int i = 0; i < n; i++) {
            if(repeatIc(s,i).length()!=i*l) throw new RuntimeException();
        }
        end = System.currentTimeMillis();
        System.out.println("IterConcat: " + (end-start) + "ms");

        start = System.currentTimeMillis();
        for (int i = 0; i < n; i++) {
            if(repeatSb(s,i).length()!=i*l) throw new RuntimeException();
        }
        end = System.currentTimeMillis();
        System.out.println("IterStrB: " + (end-start) + "ms");
    }

    public static String repeatLog2(String s, int times) {
        if (times <= 0) {
            return "";
        }
        else if (times % 2 == 0) {
            return repeatLog2(s+s, times/2);
        }
        else {
           return s + repeatLog2(s+s, times/2);
        }
    }

    public static String repeatR(String s, int times) {
        if (times <= 0) {
            return "";
        }
        else {
            return s + repeatR(s, times-1);
        }
    }

    public static String repeatIc(String s, int times) {
        String tmp = "";
        for (int i = 0; i < times; i++) {
            tmp += s;
        }
        return tmp;
    }

    public static String repeatSb(String s, int n) {
        final StringBuilder sb = new StringBuilder();
        for(int i = 0; i < n; i++) {
            sb.append(s);
        }
        return sb.toString();
    }
}

It takes 2 arguments, the first is the number of iterations (each function run with repeat times arg from 1..n) and the second is the string to repeat.

So far, a quick inspection of the times running with different inputs leaves the ranking something like this (better to worse):

  1. Iterative StringBuilder append (1x).
  2. Recursive concatenation log2 invocations (~3x).
  3. Recursive concatenation linear invocations (~30x).
  4. Iterative concatenation linear (~45x).

I wouldn't ever guessed that the recursive function was faster than the for loop :-o

Have fun(ctional xD).

This contains less characters than your question

public static String repeat(String s, int n) {
    if(s == null) {
        return null;
    }
    final StringBuilder sb = new StringBuilder(s.length() * n);
    for(int i = 0; i < n; i++) {
        sb.append(s);
    }
    return sb.toString();
}

based on fortran's answer, this is a recusive version that uses a StringBuilder:

public static void repeat(StringBuilder stringBuilder, String s, int times) {
    if (times > 0) {
        repeat(stringBuilder.append(s), s, times - 1);
    }
}

public static String repeat(String s, int times) {
    StringBuilder stringBuilder = new StringBuilder(s.length() * times);
    repeat(stringBuilder, s, times);
    return stringBuilder.toString();
}

using Dollar is simple as typing:

@Test
public void repeatString() {
    String string = "abc";
    assertThat($(string).repeat(3).toString(), is("abcabcabc"));
}

PS: repeat works also for array, List, Set, etc

using only JRE classes (System.arraycopy) and trying to minimize the number of temp objects you can write something like:

public static String repeat(String toRepeat, int times) {
    if (toRepeat == null) {
        toRepeat = "";
    }

    if (times < 0) {
        times = 0;
    }

    final int length = toRepeat.length();
    final int total = length * times;
    final char[] src = toRepeat.toCharArray();
    char[] dst = new char[total];

    for (int i = 0; i < total; i += length) {
        System.arraycopy(src, 0, dst, i, length);
    }

    return String.copyValueOf(dst);
}

EDIT

and without loops you can try with:

public static String repeat2(String toRepeat, int times) {
    if (toRepeat == null) {
        toRepeat = "";
    }

    if (times < 0) {
        times = 0;
    }

    String[] copies = new String[times];
    Arrays.fill(copies, toRepeat);
    return Arrays.toString(copies).
              replace("[", "").
              replace("]", "").
              replaceAll(", ", "");
}

EDIT 2

using Collections is even shorter:

public static String repeat3(String toRepeat, int times) {
    return Collections.nCopies(times, toRepeat).
           toString().
           replace("[", "").
           replace("]", "").
           replaceAll(", ", "");
}

however I still like the first version.

Not the shortest, but (i think) the fastest way is to use the StringBuilder:

 /**
   * Repeat a String as many times you need.
   *
   * @param i - Number of Repeating the String.
   * @param s - The String wich you want repeated.
   * @return The string n - times.
   */
  public static String repeate(int i, String s) {
    StringBuilder sb = new StringBuilder();
    for (int j = 0; j < i; j++)
      sb.append(s);
    return sb.toString();
  }

a straightforward one-line solution:
requires Java 8

Collections.nCopies( 3, "abc" ).stream().collect( Collectors.joining() );

If you are worried about performance, just use a StringBuilder inside the loop and do a .toString() on exit of the Loop. Heck, write your own Util Class and reuse it. 5 Lines of code max.

Try this out:

public static char[] myABCs = {'a', 'b', 'c'};
public static int numInput;
static Scanner in = new Scanner(System.in);

public static void main(String[] args) {
    System.out.print("Enter Number of Times to repeat: ");
    numInput = in.nextInt();
    repeatArray(numInput);
}

public static int repeatArray(int y) {
    for (int a = 0; a < y; a++) {
        for (int b = 0; b < myABCs.length; b++) {
            System.out.print(myABCs[b]);                
        }
        System.out.print(" ");
    }
    return y;
}

If you only know the length of the output string (and it may be not divisible by the length of the input string), then use this method:

static String repeat(String s, int length) {
    return s.length() >= length ? s.substring(0, length) : repeat(s + s, length);
}

Usage demo:

for (int i = 0; i < 50; i++)
    System.out.println(repeat("_/‾\\", i));

Don't use with empty s and length > 0, since it's impossible to get the desired result in this case.

Despite your desire not to use loops, I think you should use a loop.

String repeatString(String s, int repetitions)
{
    if(repetitions < 0) throw SomeException();

    else if(s == null) return null;

    StringBuilder stringBuilder = new StringBuilder(s.length() * repetitions);

    for(int i = 0; i < repetitions; i++)
        stringBuilder.append(s);

    return stringBuilder.toString();
}

Your reasons for not using a for loop are not good ones. In response to your criticisms:

  1. Whatever solution you use will almost certainly be longer than this. Using a pre-built function only tucks it under more covers.
  2. Someone reading your code will have to figure out what you're doing in that non-for-loop. Given that a for-loop is the idiomatic way to do this, it would be much easier to figure out if you did it with a for loop.
  3. Yes someone might add something clever, but by avoiding a for loop you are doing something clever. That's like shooting yourself in the foot intentionally to avoid shooting yourself in the foot by accident.
  4. Off-by-one errors are also mind-numbingly easy to catch with a single test. Given that you should be testing your code, an off-by-one error should be easy to fix and catch. And it's worth noting: the code above does not contain an off-by-one error. For loops are equally easy to get right.
  5. So don't reuse variables. That's not the for-loop's fault.
  6. Again, so does whatever solution you use. And as I noted before; a bug hunter will probably be expecting you to do this with a for loop, so they'll have an easier time finding it if you use a for loop.
public static String rep(int a,String k)

       {
           if(a<=0)
                return "";
           else 
           {a--;
               return k+rep(a,k);
       }

You can use this recursive method for you desired goal.

Consolidated for quick reference:

public class StringRepeat {

// Java 11 has built-in method - str.repeat(3);
// Apache - StringUtils.repeat(3);
// Google - Strings.repeat("",n);
// System.arraycopy

static String repeat_StringBuilderAppend(String str, int n) {

    if (str == null || str.isEmpty())
        return str;

    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < n; i++) {
        sb.append(str);
    }
    return sb.toString();
}

static String repeat_ArraysFill(String str, int n) {
    String[] strs = new String[n];
    Arrays.fill(strs, str);
    return Arrays.toString(strs).replaceAll("\\[|\\]|,| ", "");
}

static String repeat_Recursion(String str, int n) {
    if (n <= 0)
        return "";
    else
        return str + repeat_Recursion(str, n - 1);
}

static String repeat_format1(String str, int n) {
    return String.format(String.format("%%%ds", n), " ").replace(" ", str);
}

static String repeat_format2(String str, int n) {
    return new String(new char[n]).replace("\0", str);
}

static String repeat_format3(String str, int n) {
    return String.format("%0" + n + "d", 0).replace("0", str);
}

static String repeat_join(String str, int n) {
    return String.join("", Collections.nCopies(n, str));
}

static String repeat_stream(String str, int n) {
    return Stream.generate(() -> str).limit(n).collect(Collectors.joining());
}

public static void main(String[] args) {
    System.out.println(repeat_StringBuilderAppend("Mani", 3));
    System.out.println(repeat_ArraysFill("Mani", 3));
    System.out.println(repeat_Recursion("Mani", 3));
    System.out.println(repeat_format1("Mani", 3));
    System.out.println(repeat_format2("Mani", 3));
    System.out.println(repeat_format3("Mani", 3));
    System.out.println(repeat_join("Mani", 3));
    System.out.println(repeat_stream("Mani", 3));

}

}

Related