Manera simple de repetir una cadena en java

598

Estoy buscando un método u operador común simple que me permita repetir algunas veces String n . Sé que podría escribir esto usando un bucle for, pero deseo evitar los bucles cuando sea necesario y debería existir un método directo simple en alguna parte.

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

repeated.equals("abcabcabc");

Relacionado con:

repetir cadena javascript Crear NSString repitiendo otra cadena un número determinado de veces

Editado

Intento evitar los bucles cuando no son completamente necesarios porque:

  1. Se suman a la cantidad de líneas de código incluso si están ocultas en otra función.

  2. Alguien que lea mi código tiene que descubrir qué estoy haciendo en ese bucle for. Incluso si está comentado y tiene nombres de variables significativos, todavía tienen que asegurarse de que no está haciendo nada "inteligente".

  3. A los programadores les encanta poner cosas inteligentes en bucles, incluso si lo escribo para "hacer solo lo que está destinado a hacer", eso no impide que alguien venga y agregue alguna "solución" inteligente adicional.

  4. A menudo es fácil equivocarse. Para los bucles que involucran índices tienden a generarse por errores de uno.

  5. Para los bucles, a menudo se reutilizan las mismas variables, lo que aumenta la posibilidad de encontrar errores de alcance realmente difíciles.

  6. Para los bucles, aumente el número de lugares que debe buscar un cazador de insectos.

Ethan Heilman
fuente
35
Entiendo que los bucles pueden causar algunos problemas reales. Pero no debe intentar evitar los bucles "a toda costa" porque si le cuesta legibilidad, facilidad de mantenimiento y velocidad, está siendo contraproducente. Este es uno de esos casos.
Imagist
99
"Se suman a la cantidad de líneas de código incluso si están escondidas en otra función" ... wow, simplemente wow. Big-O, no
LoC
77
@imagist Estoy evitando bucles en situaciones en las que me cuesta legibilidad y facilidad de mantenimiento. Considero que la velocidad es el problema menos importante aquí (de hecho, no es un problema). Creo que los bucles se usan en exceso y estoy tratando de aprender a usarlos solo cuando son necesarios y no como una solución predeterminada.
Ethan Heilman el
3
@Pirolística No estoy reclamando rendimiento o beneficios asintóticos. En lugar de decir que escribiendo menos código y usando funciones de biblioteca en lugar de reinventar la rueda, reduzco el área de superficie del error (Líneas de código) al tiempo que aumenta la legibilidad. Ambas cosas buenas, estoy seguro de que estarás de acuerdo.
Ethan Heilman el
44
@ e5; perdón por publicar años después. Encuentro esta pregunta muy apropiada. Si se inserta en un método, los argumentos deben ser probados (veces> = 0), errores arrojados, etc. Esto agrega robustez pero también líneas de código para leer. Repetir una cadena es algo inequívoco. Quien lee el código sabe exactamente lo que hace una cadena. Repetir, incluso sin una línea de comentario o javadoc. Si usamos una biblioteca estable, es razonable pensar que una función tan simple no tiene errores, TODAVÍA introduce alguna forma de verificación de "robustez" de la que incluso debemos preocuparnos. Si pudiera pedir 10 mejoras, este (tipo de) cosas sería una.
AgostinoX

Respuestas:

230

String::repeat

". ".repeat( 7 )  // Seven period-with-space pairs: . . . . . . . 

Nuevo en Java 11 es el método String::repeatque hace exactamente lo que pediste:

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

Su Javadoc dice:

/**
 * Returns a string whose value is the concatenation of this
 * string repeated {@code count} times.
 * <p>
 * If this string is empty or count is zero then the empty
 * string is returned.
 *
 * @param count number of times to repeat
 *
 * @return A string composed of this string repeated
 * {@code count} times or the empty string if this
 * string is empty or count is zero
 *
 * @throws IllegalArgumentException if the {@code count} is
 * negative.
 *
 * @since 11
 */ 
Nicolai
fuente
2
Código fuente de @Nicolai, por si a alguien le importa hg.openjdk.java.net/jdk/jdk/file/fc16b5f193c7/src/java.base/…
Eugene
77
Ni siquiera he visto Java 9 en la calle (y no lo haré por mucho tiempo ...) - y aparentemente 11 está listo para ser
enviado
1
Probablemente obvio, pero también puede llamar a este método en un literal de cadena:"abc".repeat(3)
Boann
895

Aquí está la versión más corta (se requiere Java 1.5+):

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

¿Dónde nestá el número de veces que desea repetir la cadena y ses la cadena a repetir?

No se necesitan importaciones ni bibliotecas.

usuario102008
fuente
22
No creo que esté ofuscado en absoluto. Los tipos primitivos ( char[]en este caso) se Stringcrean instancias con nulos, luego se crea un a partir de char[], y los nulos están replaced()con el personaje que deseas
Amarok
32
Si bien esto es muy inteligente (+1), creo que prueba bastante el punto de que los bucles a menudo crean un código más claro
Richard Tingle el
75
Para aquellos que se quejan de la ofuscación, la legibilidad depende de la alfabetización. Esto está perfectamente claro en lo que hace y es educativo para aquellos que pueden no verlo de inmediato. Esto es lo que obtienes con Java por cierto.
dansalmo
11
Para un mejor rendimiento se ...replace('\0', str)debe utilizar en lugar de la versión de cadena.
user686249
11
@ user686249: Solo hay replace(char oldChar, char newChar)y replace(CharSequence target, CharSequence replacement)no veo cómo podría funcionar
eso
334

Si está utilizando Java <= 7 , esto es tan "conciso" como se pone:

// create a string made up of n copies of string s
String.format("%0" + n + "d", 0).replace("0", s);

En Java 8 y superior hay una forma más legible:

// create a string made up of n copies of string s
String.join("", Collections.nCopies(n, s));

Finalmente, para Java 11 y superior, hay un nuevo repeat​(int count)método específicamente para esto ( enlace )

"abc".repeat(12);

Alternativamente, si su proyecto usa bibliotecas java, hay más opciones.

Para Apache Commons :

StringUtils.repeat("abc", 12);

Para Google Guava :

Strings.repeat("abc", 12);
Caner
fuente
44
El primero causa una excepción cuando nes cero.
saka1029
El ejemplo de Java 8 no se compila -> No coinciden los tipos: no se puede convertir de List <Character> a CharSequence
Arigion
66
@Arigion stiene que ser String, no unChar
Caner
@Caner Gracias. Mi mal, me disculpo. Aparentemente estaba muy cansado ayer. Perdón por el voto negativo. Eliminaré el voto a favor tan pronto como pueda (está bloqueado hasta que se
edite la
@Arigion no hay problema, dejó en claro ahora que s es una cadena
Caner
312

Commons Lang StringUtils.repeat ()

Uso:

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

repeated.equals("abcabcabc");
ChssPly76
fuente
99
utilizando un método de una sola dependencia por el bien de la simplicidad, a la larga puede que resulta en un frasco-demonios
DFA
81
Claro, excepto que es commons lang. No creo haber visto nunca un proyecto de más de 5000 LOCS que no tuviera commons lang.
Ethan Heilman el
11
Commons Lang es de código abierto, descárgalo y échale un vistazo. Por supuesto, tiene un bucle dentro, pero no es tan simple. Se realizó un gran esfuerzo para perfilar y optimizar esa implementación.
ChssPly76
28
No evito los bucles por razones de rendimiento (lea mis razones en la pregunta). Cuando alguien ve StringUtils.repeat, sabe lo que estoy haciendo. No tienen que preocuparse de que haya intentado escribir mi propia versión de repetición y haya cometido un error. ¡Es una unidad cognitiva atómica!
Ethan Heilman el
77
@ Thorbjørn Ravn Andersen - se puede conseguir mucho más interesante si las cosas siguen siendo sacadas de contexto
ChssPly76
140

Java 8's String.joinproporciona una forma ordenada de hacer esto junto con Collections.nCopies:

// say hello 100 times
System.out.println(String.join("", Collections.nCopies(100, "hello")));
Boann
fuente
77
¡gracias! Para Android, TextUtils.join () se puede usar en lugar de String.join ()
MinaHany
2
Gracias por esta respuesta ¡Parece ser la forma más limpia sin usar ningún método de utilidad API oder externo! ¡¡muy bien!!
Andreas M. Oberheim
2
Lo bueno de este método es que con join puede proporcionar un carácter separador que resulta muy útil si, por ejemplo, está creando una lista CSV. Con todos los demás métodos, tiene un carácter de unión de terminación que debe eliminarse en una operación separada.
DroidOS
102

Aquí hay una manera de hacerlo usando solo funciones de cadena estándar y sin bucles explícitos:

// create a string made up of  n  copies of  s
repeated = String.format(String.format("%%%ds", n), " ").replace(" ",s);
IJ Kennedy
fuente
55
Increíble :-) ¡Aunque ten cuidado de que n se convierta en cero ...!
Yang Meyer
44
@Vijay Dev & fortran: No, quiso decir replace(). En Java 1.5+, hay una versión sobrecargada de replace()dos CharSequencesegundos (que incluye Strings): download.oracle.com/javase/1.5.0/docs/api/java/lang/…
user102008
79
Ay. Esto es feo
Karel Bílek
8
@mzuba, digamos n=3: primero formatea una cadena para que se vea algo así %03d( %%es para escapar del signo de porcentaje), que es el código de formato para agregar 3 ceros de relleno, luego formatea 0con eso, lo que lleva a 000, y finalmente reemplaza cada uno 0con las cadenas
fortran
15
Puede hacer que la solución sea menos fea y más fácil de entender: String.format ("% 0" + n + "d", 0) .replace ("0", s)
Artur
87

Si eres como yo y quieres usar Google Guava y no Apache Commons. Puede usar el método de repetición en la clase Guava Strings.

Strings.repeat("-", 60);
Jack
fuente
2
... y obtén 3Mb de nuevas dependencias.
MonoThreaded
66
@MonoThreaded Pensé que sería evidente, pero no incluyas guayaba solo para repetir una secuencia. Mi respuesta fue sobre si ya estás usando guayaba de todos modos, así es como lo harías.
Jack
53

Con , también puedes usar Stream.generate.

import static java.util.stream.Collectors.joining;
...
String repeated = Stream.generate(() -> "abc").limit(3).collect(joining()); //"abcabcabc"

y puede envolverlo en un método de utilidad simple si es necesario:

public static String repeat(String str, int times) {
   return Stream.generate(() -> str).limit(times).collect(joining());
}
Alexis C.
fuente
66
... o return IntStream.range(0, times).mapToObj(i -> str).collect(joining());que paraleliza mejor
Alexis C.
32

¿Entonces quieres evitar bucles?

Aqui lo tienes:

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

(por supuesto, sé que esto es feo e ineficiente, pero no tiene bucles :-p)

¿Quieres que sea más simple y bonito? usa jython:

s * 3

Editar : optimicemos un poco :-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 : he hecho un punto de referencia rápido y sucio para las 4 alternativas principales, pero no tengo tiempo para ejecutarlo varias veces para obtener los medios y trazar los tiempos para varias entradas ... Así que aquí está el código si alguien quiere intentarlo:

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

Se necesitan 2 argumentos, el primero es el número de iteraciones (cada función se ejecuta con tiempos de repetición arg desde 1..n) y el segundo es la cadena para repetir.

Hasta ahora, una inspección rápida de los tiempos que se ejecutan con diferentes entradas deja el ranking algo así (de mejor a peor):

  1. StringBuilder iterativo anexar (1x).
  2. Invocaciones de log2 de concatenación recursiva (~ 3x).
  3. Invocaciones lineales de concatenación recursiva (~ 30x).
  4. Concatenación iterativa lineal (~ 45x).

Nunca adiviné que la función recursiva era más rápida que el forbucle: -o

Diviértete (ctional xD).

fortran
fuente
1
+1 para la recursión y obviamente ser un hacker de lisp. Tampoco creo que esto sea tan ineficiente, la concatenación de cadenas no es el crimen de guerra que alguna vez fue, porque + realmente es solo un UTH de stringBuilder. Consulte stackoverflow.com/questions/47605/java-string-concatenation y schneide.wordpress.com/2009/02/23/… . Me pregunto cuánto empuja y sale toda esa pila del costo de recursión, o si el punto de acceso se encarga de ellos. Realmente desearía tener el tiempo libre para compararlo. ¿Alguien más tal vez?
Ethan Heilman el
@ e5: fortran tiene razón; Esta solución podría hacerse más eficiente. Esta implementación creará innecesariamente un nuevo StringBuilder (y un nuevo String) para cada recursión. Sin embargo, sigue siendo una buena solución.
robar el
3
@ e5 Me gustaría ser un hacker Lisp xD ... Si lo fuera, habría utilizado una cola recursiva función :-p
FORTRAN
1
Las microbenchmarks no funcionan bien en Java. Intentar medir la velocidad de sus implementaciones de esa manera no es bueno.
ceklock
@tecnotron Lo sé, pero aún así son mejores que nada ... Y la única "sorpresa" fue la ligera diferencia entre la concatenación de bucles ingenua y la recursividad lineal.
fortran
20

Este contiene menos caracteres que tu pregunta

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();
}
Pirolista
fuente
44
Contiene más caracteres que mi respuesta StringUtils.repeat (str, n).
Ethan Heilman el
8
A menos que ya esté usando Apache Commons, esta respuesta es mucho menos molesta: no descargar otra biblioteca, incluirla en su classpath, asegurarse de que su licencia sea compatible con la suya, etc.
Paul Tomblin
77
Por favor, nunca devuelva nulo; en ese caso, devuelva una cadena vacía, lo que le permite utilizar siempre el valor devuelto sin marcar. De lo contrario, recomendaría usar el póster.
Thorbjørn Ravn Andersen
77
Bueno, hay tres formas de manejar si s es nulo. 1. Pasar el error (devolver nulo), 2. Ocultar el error (devolver ""), 3. Lanzar un NPE. Ocultar el error y lanzar un NPE no es genial, así que pasé el error.
Pirolítico el
1
@EthanHeilman agrega el valor de 2 MB commons-lang3.3.1-sourcesy ya no eres tan bueno;) Pero si alguien ya lo ha hecho commons-lang, apoyo tu respuesta.
TWiStErRob
9

basado en la respuesta de fortran, esta es una versión recusiva que utiliza un 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();
}
dfa
fuente
1
el bucle en lugar de la recursión reduciría el número de cuadros de pila para grandes cantidades de repeticiones.
La-comadreja
7

Usar Dollar es simple como escribir:

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

PD: repetir funciona también para array, List, Set, etc.

dfa
fuente
3
¿Es realmente necesario el método afirmar que ()?
ceklock
7

Quería una función para crear una lista delimitada por comas de signos de interrogación para fines de JDBC, y encontré esta publicación. Entonces, decidí tomar dos variantes y ver cuál funcionó mejor. Después de 1 millón de iteraciones, el StringBuilder de la variedad de jardín tardó 2 segundos (fun1), y la versión críptica supuestamente más óptima (fun2) tomó 30 segundos. ¿Qué sentido tiene volver a ser críptico?

private static String fun1(int size) {
    StringBuilder sb = new StringBuilder(size * 2);
    for (int i = 0; i < size; i++) {
        sb.append(",?");
    }
    return sb.substring(1);
}

private static String fun2(int size) {
    return new String(new char[size]).replaceAll("\0", ",?").substring(1);
}
Beto
fuente
3
Tengo sentido que el segundo llevaría mucho más tiempo. Realiza una búsqueda de cadena y luego modifica la cadena carácter por carácter.
Ethan Heilman
7

Solución OOP

Casi todas las respuestas proponen una función estática como solución, pero pensando en Orientación a objetos (para fines de reutilización y claridad) se me ocurrió una Solución por delegación a través de la interfaz CharSequence (que también abre la usabilidad en clases de secuencia de caracteres mutables).

La siguiente clase se puede usar con o sin Separator-String / CharSequence y cada llamada a "toString ()" construye la última cadena repetida. ¡La entrada / separador no solo se limita a String-Class, sino que puede ser cualquier clase que implemente CharSequence (por ejemplo, StringBuilder, StringBuffer, etc.)!

Código fuente:

/**
 * Helper-Class for Repeating Strings and other CharSequence-Implementations
 * @author Maciej Schuttkowski
 */
public class RepeatingCharSequence implements CharSequence {
    final int count;
    CharSequence internalCharSeq = "";
    CharSequence separator = "";
    /**
     * CONSTRUCTOR - RepeatingCharSequence
     * @param input CharSequence to repeat
     * @param count Repeat-Count
     */
    public RepeatingCharSequence(CharSequence input, int count) {
        if(count < 0)
            throw new IllegalArgumentException("Can not repeat String \""+input+"\" less than 0 times! count="+count);
        if(count > 0)
            internalCharSeq = input;
        this.count = count;
    }
    /**
     * CONSTRUCTOR - Strings.RepeatingCharSequence
     * @param input CharSequence to repeat
     * @param count Repeat-Count
     * @param separator Separator-Sequence to use
     */
    public RepeatingCharSequence(CharSequence input, int count, CharSequence separator) {
        this(input, count);
        this.separator = separator;
    }

    @Override
    public CharSequence subSequence(int start, int end) {
        checkBounds(start);
        checkBounds(end);
        int subLen = end - start;
        if (subLen < 0) {
            throw new IndexOutOfBoundsException("Illegal subSequence-Length: "+subLen);
        }
        return (start == 0 && end == length()) ? this
                    : toString().substring(start, subLen);
    }
    @Override
    public int length() {
        //We return the total length of our CharSequences with the separator 1 time less than amount of repeats:
        return count < 1 ? 0
                : ( (internalCharSeq.length()*count) + (separator.length()*(count-1)));
    }
    @Override
    public char charAt(int index) {
        final int internalIndex = internalIndex(index);
        //Delegate to Separator-CharSequence or Input-CharSequence depending on internal index:
        if(internalIndex > internalCharSeq.length()-1) {
            return separator.charAt(internalIndex-internalCharSeq.length());
        }
        return internalCharSeq.charAt(internalIndex);
    }
    @Override
    public String toString() {
        return count < 1 ? ""
                : new StringBuilder(this).toString();
    }

    private void checkBounds(int index) {
        if(index < 0 || index >= length())
            throw new IndexOutOfBoundsException("Index out of Bounds: "+index);
    }
    private int internalIndex(int index) {
        // We need to add 1 Separator-Length to total length before dividing,
        // as we subtracted one Separator-Length in "length()"
        return index % ((length()+separator.length())/count);
    }
}

Ejemplo de uso:

public static void main(String[] args) {
    //String input = "12345";
    //StringBuffer input = new StringBuffer("12345");
    StringBuilder input = new StringBuilder("123");
    //String separator = "<=>";
    StringBuilder separator = new StringBuilder("<=");//.append('>');
    int repeatCount = 2;

    CharSequence repSeq = new RepeatingCharSequence(input, repeatCount, separator);
    String repStr = repSeq.toString();

    System.out.println("Repeat="+repeatCount+"\tSeparator="+separator+"\tInput="+input+"\tLength="+input.length());
    System.out.println("CharSeq:\tLength="+repSeq.length()+"\tVal="+repSeq);
    System.out.println("String :\tLength="+repStr.length()+"\tVal="+repStr);

    //Here comes the Magic with a StringBuilder as Input, as you can append to the String-Builder
    //and at the same Time your Repeating-Sequence's toString()-Method returns the updated String :)
    input.append("ff");
    System.out.println(repSeq);
    //Same can be done with the Separator:
    separator.append("===").append('>');
    System.out.println(repSeq);
}

Ejemplo de salida:

Repeat=2    Separator=<=    Input=123   Length=3
CharSeq:    Length=8    Val=123<=123
String :    Length=8    Val=123<=123
123ff<=123ff
123ff<====>123ff
Maciej Schuttkowski
fuente
44
rara vez he visto cosas tan desagradables: /
Ven
6

usando solo clases JRE ( System.arraycopy ) e intentando minimizar la cantidad de objetos temporales, puede escribir algo como:

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

EDITAR

y sin bucles puedes probar con:

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(", ", "");
}

EDITAR 2

usar Colecciones es aún más corto:

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

Sin embargo, todavía me gusta la primera versión.

dfa
fuente
66
-1: demasiado inteligente a la mitad. Si su objetivo es hacer que el código sea legible o eficiente, estas "soluciones" no son una buena idea. 'repetir' simplemente podría reescribirse usando un StringBuilder (configurando la capacidad inicial). Y 'repeat2' / 'repeat3' son realmente ineficientes y dependen de la sintaxis no especificada de la cadena producida por String []. ToString ().
Stephen C
@Thorb: absolutamente, con este código no puedes usar "metacaracteres", [],
dfa
@Stephen: la pregunta se editó para solicitar explícitamente ningún bucle. Ya se proporcionó una respuesta basada en StringBuilder, así que evité publicar un duplicado
dfa
@Stephan: No puedo entender el voto negativo. Mi respuesta editada está libre de bucles según lo solicitado. No hay solicitudes de eficiencia. Creo que esta pregunta es solo un esfuerzo intelectual para producir una concatenación sin bucle.
dfa el
@Stephan: la cadena producida a través de Collection.toString (y Arrays.toString) se especifica claramente en AbstractCollection.toString: "La representación de cadena consiste en una lista de los elementos de la colección en el orden en que los devuelve su iterador, encerrados entre corchetes ( "[]"). Los elementos adyacentes están separados por los caracteres "," (coma y espacio) ".
dfa
6

No es el más corto, pero (creo) la forma más rápida es usar el 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();
  }
Simón
fuente
5

Si le preocupa la velocidad, entonces debe usar la menor cantidad de copia de memoria posible. Por lo tanto, se requiere trabajar con matrices de caracteres.

public static String repeatString(String what, int howmany) {
    char[] pattern = what.toCharArray();
    char[] res = new char[howmany * pattern.length];
    int length = pattern.length;
    for (int i = 0; i < howmany; i++)
        System.arraycopy(pattern, 0, res, i * length, length);
    return new String(res);
}

Para probar la velocidad, un método óptimo similar usando StirngBuilder es el siguiente:

public static String repeatStringSB(String what, int howmany) {
    StringBuilder out = new StringBuilder(what.length() * howmany);
    for (int i = 0; i < howmany; i++)
        out.append(what);
    return out.toString();
}

y el código para probarlo:

public static void main(String... args) {
    String res;
    long time;

    for (int j = 0; j < 1000; j++) {
        res = repeatString("123", 100000);
        res = repeatStringSB("123", 100000);
    }

    time = System.nanoTime();
    res = repeatString("123", 1000000);
    time = System.nanoTime() - time;
    System.out.println("elapsed repeatString: " + time);

    time = System.nanoTime();
    res = repeatStringSB("123", 1000000);
    time = System.nanoTime() - time;
    System.out.println("elapsed repeatStringSB: " + time);

}

Y aquí los resultados de la ejecución de mi sistema:

elapsed repeatString: 6006571
elapsed repeatStringSB: 9064937

Tenga en cuenta que la prueba de bucle es patear JIT y tener resultados óptimos.

Panayotis
fuente
4

en aras de la legibilidad y portabilidad:

public String repeat(String str, int count){
    if(count <= 0) {return "";}
    return new String(new char[count]).replace("\0", str);
}
Martin Zeitler
fuente
3

Si le preocupa el rendimiento, simplemente use un StringBuilder dentro del bucle y haga un .toString () al salir del bucle. Diablos, escribe tu propia Clase de Util y reutilízala. 5 líneas de código máx.

WolfmanDragon
fuente
2

Realmente disfruto esta pregunta. Hay muchos conocimientos y estilos. Así que no puedo dejarlo sin mostrar mi rock and roll;)

{
    String string = repeat("1234567890", 4);
    System.out.println(string);
    System.out.println("=======");
    repeatWithoutCopySample(string, 100000);
    System.out.println(string);// This take time, try it without printing
    System.out.println(string.length());
}

/**
 * The core of the task.
 */
@SuppressWarnings("AssignmentToMethodParameter")
public static char[] repeat(char[] sample, int times) {
    char[] r = new char[sample.length * times];
    while (--times > -1) {
        System.arraycopy(sample, 0, r, times * sample.length, sample.length);
    }
    return r;
}

/**
 * Java classic style.
 */
public static String repeat(String sample, int times) {
    return new String(repeat(sample.toCharArray(), times));
}

/**
 * Java extreme memory style.
 */
@SuppressWarnings("UseSpecificCatch")
public static void repeatWithoutCopySample(String sample, int times) {
    try {
        Field valueStringField = String.class.getDeclaredField("value");
        valueStringField.setAccessible(true);
        valueStringField.set(sample, repeat((char[]) valueStringField.get(sample), times));
    } catch (Exception ex) {
        throw new RuntimeException(ex);
    }
}

¿Te gusta?

Daniel De León
fuente
1
En mi prueba más extrema, produzco una longitud de repetición de cadena de 1,700,000,000 (1.7 gigas), usando -Xms4937m
Daniel De León
2
public static String repeat(String str, int times) {
    int length = str.length();
    int size = length * times;
    char[] c = new char[size];
    for (int i = 0; i < size; i++) {
        c[i] = str.charAt(i % length);
    }
    return new String(c);
}
halcón
fuente
2

Bucle simple

public static String repeat(String string, int times) {
    StringBuilder out = new StringBuilder();
    while (times-- > 0) {
        out.append(string);
    }
    return out.toString();
}
Codler
fuente
2
Pase timesal constructor StringBuilder.
Behrouz.M
2

Probar esto:

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

fuente
2

Usando la recursividad, puede hacer lo siguiente (usando operadores ternarios, una línea como máximo):

public static final String repeat(String string, long number) {
    return number == 1 ? string : (number % 2 == 0 ? repeat(string + string, number / 2) : string + repeat(string + string, (number - 1) / 2));
}

Lo sé, es feo y probablemente no eficiente, ¡pero es una línea!

Hiperneutrino
fuente
Este es el enfoque que tomaría, pero ¿por qué hacer más controles de los necesarios? número de retorno> 0? cadena + repetición (cadena, número-1): "";
Fering
Oh, parece que niczm25 respondió con esto a continuación
Fering
@Fering razón principal para que de esta manera sea O (log N) promedio en lugar de O (N) siempre. Ligeramente más optimización que la otra, aunque aún así es malo.
HyperNeutrino
2

Una solución sencilla de una línea:
requiere Java 8

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

A pesar de su deseo de no usar bucles, creo que debería usar un bucle.

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

Sus razones para no usar un bucle for no son buenas. En respuesta a tus críticas:

  1. Cualquier solución que use seguramente será más larga que esta. El uso de una función preconstruida solo lo oculta debajo de más cubiertas.
  2. Alguien que lea su código tendrá que descubrir lo que está haciendo en ese no for-loop. Dado que un bucle for es la forma idiomática de hacer esto, sería mucho más fácil darse cuenta si lo hiciera con un bucle for.
  3. Si alguien podría agregar algo inteligente, pero evitando un bucle que están haciendo algo inteligente . Eso es como pegarse un tiro en el pie intencionalmente para evitar dispararse en el pie por accidente.
  4. Los errores fuera de uno también son increíblemente fáciles de detectar con una sola prueba. Dado que debería estar probando su código, un error fuera de uno debería ser fácil de solucionar y detectar. Y vale la pena señalar: el código anterior no contiene un error off-by-one. Para los bucles son igualmente fáciles de acertar.
  5. Así que no reutilices las variables. Eso no es culpa del bucle for.
  6. De nuevo, también lo hace cualquier solución que use. Y como señalé antes; un cazador de errores probablemente esperará que hagas esto con un bucle for, por lo que será más fácil encontrarlo si usas un bucle for.
Imaginista
fuente
3
-1. Aquí hay dos ejercicios para usted: a) ejecute su código con repetitions = -5. b) descargar Commons Lang y ejecutar repeatString('a', 1000)un millón de veces en un bucle; haz lo mismo con tu código; compara los tiempos. Para obtener crédito adicional, haga lo mismo con repeatString('ab', 1000).
ChssPly76
2
¿Estás argumentando que tu código es más legible entonces StringUtils.repeat("ab",1000)? Porque esa fue mi respuesta que has rechazado. También funciona mejor y no tiene errores.
ChssPly76
2
Lee la segunda oración en la pregunta que estás citando. "Intento evitar los bucles a toda costa porque" se agregó a la pregunta como una aclaración en respuesta a la respuesta de Andrew Hare después de mi respuesta, no es que sea importante porque si la posición que está tomando es "la respuesta es mala si el bucle es usado en cualquier lugar "no hay respuestas a la pregunta de OP. Incluso las soluciones de dfa, ingeniosas como son, se utilizan para bucles en el interior. "jar hell" fue respondido arriba; commons lang se usa en todas las aplicaciones de tamaño decente de todos modos y, por lo tanto, no agrega una nueva dependencia.
ChssPly76
2
@ ChssPly76 en este punto, estoy bastante seguro de que el imaginista está trolleando. Realmente me cuesta ver cómo alguien puede leer lo que escribí y pensar seriamente en las respuestas escritas anteriormente.
Ethan Heilman el
1
@ ChssPly76 mis respuestas no tienen lazos en todas :-p
FORTRAN
1

Si solo conoce la longitud de la cadena de salida (y puede no ser divisible por la longitud de la cadena de entrada), utilice este método:

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

Demostración de uso:

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

No utilice con vacío sy length> 0, ya que es imposible obtener el resultado deseado en este caso.

John McClane
fuente
0

Aquí está el último Stringutils.java StringUtils.java

    public static String repeat(String str, int repeat) {
    // Performance tuned for 2.0 (JDK1.4)

    if (str == null) {
        return null;
    }
    if (repeat <= 0) {
        return EMPTY;
    }
    int inputLength = str.length();
    if (repeat == 1 || inputLength == 0) {
        return str;
    }
    if (inputLength == 1 && repeat <= PAD_LIMIT) {
        return repeat(str.charAt(0), repeat);
    }

    int outputLength = inputLength * repeat;
    switch (inputLength) {
        case 1 :
            return repeat(str.charAt(0), repeat);
        case 2 :
            char ch0 = str.charAt(0);
            char ch1 = str.charAt(1);
            char[] output2 = new char[outputLength];
            for (int i = repeat * 2 - 2; i >= 0; i--, i--) {
                output2[i] = ch0;
                output2[i + 1] = ch1;
            }
            return new String(output2);
        default :
            StringBuilder buf = new StringBuilder(outputLength);
            for (int i = 0; i < repeat; i++) {
                buf.append(str);
            }
            return buf.toString();
    }
    }

ni siquiera necesita ser tan grande, puede convertirse en esto, y puede copiarse y pegarse en una clase de utilidad en su proyecto.

    public static String repeat(String str, int num) {
    int len = num * str.length();
    StringBuilder sb = new StringBuilder(len);
    for (int i = 0; i < times; i++) {
        sb.append(str);
    }
    return sb.toString();
    }

Entonces, e5, creo que la mejor manera de hacer esto sería simplemente usar el código mencionado anteriormente, o cualquiera de las respuestas aquí. pero commons lang es demasiado grande si es un proyecto pequeño

alexmherrmann
fuente
No creo que haya mucho más que puedas hacer ... ¡exceder quizás MUCHO!
alexmherrmann
0

Creé un método recursivo que hace lo mismo que quieres ... no dudes en usar esto ...

public String repeat(String str, int count) {
    return count > 0 ?  repeat(str, count -1) + str: "";
}

Tengo la misma respuesta en ¿Puedo multiplicar cadenas en Java para repetir secuencias?

niczm25
fuente
Reasignación innecesaria de cadenas y sobrecarga de recursividad ... malo, malo, no bueno.
Alexander - Restablece a Mónica el
1
Esto será lento ¡No recomendado! Usar en su StringBuilderlugar.
Svetlin Nakov
0
public static String rep(int a,String k)

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

Puede utilizar este método recursivo para su objetivo deseado.

Syed Salman Hassan
fuente