parte - Una forma sencilla de repetir una cadena en java
manejo de cadenas en java (29)
Solución OOP
Casi todas las respuestas proponen una función estática como solución, pero pensando en Objetos (por motivos de reutilización y claridad), se me ocurrió una Solución a través de la Delegación a través de la Interfaz de Secuencia de Chars (que también abre la facilidad de uso en Clases de CharSecuencia mutables).
La siguiente clase se puede usar con o sin Separator-String / CharSequence y cada llamada a "toString ()" crea la String final 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
Estoy buscando un simple método u operador común que me permita repetir algunos String n veces. Sé que podría escribir esto usando un bucle for, pero deseo evitar los bucles siempre que sea necesario y debería existir un método directo simple en algún lugar.
String str = "abc";
String repeated = str.repeat(3);
repeated.equals("abcabcabc");
Relacionado con:
repetir cadena javascript Crear NSString repitiendo otra cadena un número dado de veces
Editado
Intento evitar los bucles cuando no son completamente necesarios porque:
Se agregan al número de líneas de código incluso si están ocultas en otra función.
Alguien que lea mi código tiene que averiguar qué estoy haciendo en ese bucle for. Incluso si está comentado y tiene nombres de variables significativas, todavía tienen que asegurarse de que no está haciendo nada "inteligente".
A los programadores les encanta poner cosas inteligentes en los bucles, incluso si escribo "para hacer solo lo que se pretende que haga", eso no impide que alguien venga y agregue alguna "solución" inteligente adicional.
Muy a menudo son fáciles de equivocarse. Para los bucles que involucran índices tienden a generarse por un error.
Para los bucles, a menudo se reutilizan las mismas variables, lo que aumenta la posibilidad de encontrar errores de alcance realmente difíciles.
Para los bucles, aumentar el número de lugares que un cazador de errores tiene que buscar.
¿Así que quieres evitar los 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)
¿Lo quieres más sencillo y bonito? usa jython:
s * 3
Edit : vamos a optimizarlo 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 de 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();
}
}
Toma 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 a repetir.
Hasta ahora, una inspección rápida de los tiempos que corren con diferentes entradas deja el ranking algo como esto (de mejor a peor):
- Anexado de StringBuilder adjunto (1x).
- Invocaciones de log2 de concatenación recursiva (~ 3x).
- Invocaciones lineales de concatenación recursiva (~ 30x).
- Concatenación iterativa lineal (~ 45x).
Nunca hubiera imaginado que la función recursiva fuera más rápida que el bucle for
: -o
Divertirse (ctional xD).
A partir de Java 11, hay un método String::repeat
que 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
*/
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 sus críticas:
- Cualquiera que sea la solución que use, seguramente será más larga que esta. El uso de una función preconstruida solo la coloca bajo más cubiertas.
- Alguien que lea su código tendrá que averiguar qué está haciendo en ese bucle no for-for. Dado que un for-loop es la forma idiomática de hacer esto, sería mucho más fácil averiguar si lo hiciste con un for for.
- Si alguien podría agregar algo inteligente, pero evitando un bucle que está haciendo algo inteligente . Es como dispararse intencionalmente en el pie para evitar dispararte a ti mismo en el pie por accidente.
- Los errores off-by-one también son fáciles de detectar con una sola prueba. Dado que debería estar probando su código, un error off-by-one debería ser fácil de corregir y detectar. Y vale la pena señalarlo: el código anterior no contiene un error de apagado por uno. Para los bucles son igualmente fáciles de hacer bien.
- Así que no reutilice las variables. Eso no es culpa del for-loop.
- Una vez más, lo mismo ocurre con cualquier solución que uses. 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.
Aquí está la versión más corta (se requiere Java 1.5+):
repeated = new String(new char[n]).replace("/0", s);
Donde n
es el número de veces que desea repetir la cadena y s
es la cadena para repetir.
No hay importaciones o bibliotecas necesarias.
Aquí hay una forma de hacerlo utilizando 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);
Bucle simple
public static String repeat(String string, int times) {
StringBuilder out = new StringBuilder();
while (times-- > 0) {
out.append(string);
}
return out.toString();
}
Con java-8 , 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());
}
En aras de la legibilidad y la portabilidad:
public String repeat(String str, int count){
if(count <= 0) {return "";}
return new String(new char[count]).replace("/0", str);
}
Esto 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();
}
No es el más corto, pero (creo) la forma más rápida es usar 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();
}
Quería que una función creara una lista de signos de interrogación delimitada por comas para propósitos 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, la variedad de jardín StringBuilder tomó 2 segundos (fun1), y la versión críptica supuestamente más óptima (fun2) tomó 30 segundos. ¿Cuál es el punto de ser críptico de nuevo?
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);
}
Realmente disfruto esta pregunta. Hay mucho conocimiento 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?
Si eres como yo y quieres usar Google Guava y no Apache Commons. Puedes usar el método de repeat en la clase Guava Strings.
Strings.repeat("-", 60);
Si está utilizando Java <= 7 , esto es tan simple como se obtiene:
// 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 manera fácil:
// create a string made up of n copies of string s
String.join("", Collections.nCopies(n, s));
Java 11 agregó un nuevo método de repeat​(int count)
específicamente para este ( download.java.net/java/early_access/jdk11/docs/api/java.base/… )
int n = 3;
"abc".repeat(n);
Si le preocupa el rendimiento, solo use un StringBuilder dentro del bucle y haga un .toString () al salir del bucle. Heck, escribe tu propia clase de Util y reutiliza. 5 líneas de código máx.
Si le preocupa la velocidad, debe utilizar la menor cantidad posible de copias de memoria. 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 que usa 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 ejecución de mi sistema:
elapsed repeatString: 6006571
elapsed repeatStringSB: 9064937
Tenga en cuenta que la prueba de bucle es activar JIT y obtener resultados óptimos.
Usando Dollar es tan simple como escribir:
@Test
public void repeatString() {
String string = "abc";
assertThat($(string).repeat(3).toString(), is("abcabcabc"));
}
PD: la repetición funciona también para array, List, Set, etc.
Usando la recursión, puede hacer lo siguiente (usando operadores ternarios, una línea máx):
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 es eficiente, ¡pero es una línea!
basado en la respuesta de fortran, esta es una versión recusiva que usa 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();
}
utilizando solo clases JRE ( System.arraycopy ) y tratando de 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
el uso de Collections 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.
Commons Lang StringUtils.repeat ()
Uso:
String str = "abc";
String repeated = StringUtils.repeat(str, 3);
repeated.equals("abcabcabc");
String.join
Java 8 proporciona una forma ordenada de hacerlo junto con Collections.nCopies
:
// say hello 100 times
System.out.println(String.join("", Collections.nCopies(100, "hello")));
Creé un método recursivo que hace lo mismo que tú quieres ... siéntete libre de usar este ...
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?
Esta es una forma sencilla de repetir una estrella varias veces (hasta un máximo conocido):
String stars = "*****".substring(0, n);
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;
}
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, se puede convertir en esto, y se puede copiar y pegar 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 el lenguaje común es demasiado grande si se trata de un proyecto pequeño.
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);
}
repeated = str + str + str;
A veces lo simple es lo mejor. Todos los que lean el código pueden ver lo que está pasando.
Y el compilador hará las cosas elegantes con StringBuilder
detrás de escena para ti.