web-dev-qa-db-fra.com

Moyen simple de répéter une chaîne dans java

Je cherche une méthode ou un opérateur simple, qui me permet de répéter quelques fois String n . Je sais que je pourrais écrire ceci en utilisant une boucle for, mais je souhaite éviter les boucles for chaque fois que cela est nécessaire et une méthode directe simple devrait exister quelque part.

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

repeated.equals("abcabcabc");

Relatif à:

répétez la chaîne javascriptcréez NSString en répétant une autre chaîne un nombre de fois donné

Edité

J'essaie d'éviter les boucles for lorsqu'elles ne sont pas complètement nécessaires pour les raisons suivantes:

  1. Ils ajoutent au nombre de lignes de code même s'ils sont cachés dans une autre fonction.

  2. Quelqu'un qui lit mon code doit comprendre ce que je fais dans cette boucle. Même s'il est commenté et que les noms de variables sont significatifs, ils doivent toujours s'assurer qu'il ne fait rien de "malin".

  3. Les programmeurs aiment mettre des choses intelligentes dans les boucles, même si je l’écris pour "ne fais que ce qu’il est destiné à faire", cela n’empêche pas que quelqu'un vienne et ajoute quelques astuces "astucieuses".

  4. Ils sont très souvent faciles à se tromper. Les boucles impliquant des index tendent à être générées par un seul bogue.

  5. Les boucles for réutilisent souvent les mêmes variables, ce qui augmente les chances de trouver des bogues difficiles à détecter.

  6. Pour les boucles, augmenter le nombre de places qu'un chasseur de bogues doit regarder.

527
Ethan Heilman

_String::repeat_

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

Nouveau dans Java 11 est la méthode String::repeat qui fait exactement ce que vous avez demandé:

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

Son Javadoc dit:

_/**
 * 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
 */ 
_
148
Nicolai

Voici la version la plus courte (Java 1.5+ requis):

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

n est le nombre de fois que vous souhaitez répéter la chaîne et s est la chaîne à répéter.

Aucune importation ou bibliothèque nécessaire.

845
user102008

Commons Lang StringUtils.repeat ()

Usage:

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

repeated.equals("abcabcabc");
301
ChssPly76

Si vous utilisez Java <= 7 , cette opération est aussi simple que cela:

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

En Java 8 et au-dessus, il existe un moyen simple:

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

Java 11 a ajouté une nouvelle méthode repeat​(int count) spécialement pour cela ( lien )

int n = 3;
"abc".repeat(n);
266
Caner

Les versions de Java 8 String.join fournissent un moyen pratique de le faire en conjonction avec Collections.nCopies :

_// say hello 100 times
System.out.println(String.join("", Collections.nCopies(100, "hello")));
_
133
Boann

Voici une façon de le faire en utilisant uniquement les fonctions de chaîne standard et sans boucles explicites:

// create a string made up of  n  copies of  s
repeated = String.format(String.format("%%%ds", n), " ").replace(" ",s);
99
I. J. Kennedy

Si vous êtes comme moi et souhaitez utiliser Google Guava et non Apache Commons. Vous pouvez utiliser la méthode repeat dans la classe Guava Strings.

Strings.repeat("-", 60);
84
Jack

Avec Java-8 , vous pouvez également utiliser Stream.generate.

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

et vous pouvez l'envelopper dans une méthode d'utilitaire simple si nécessaire:

public static String repeat(String str, int times) {
   return Stream.generate(() -> str).limit(times).collect(joining());
}
49
Alexis C.

Alors tu veux éviter les boucles?

Ici vous l'avez:

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

(Bien sûr, je sais que c'est moche et inefficace, mais il n'y a pas de boucles :-p)

Vous voulez que ce soit plus simple et plus joli? utilisez jython:

s * 3

Edit: optimisons un peu :-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: J'ai fait une référence rapide et sale pour les 4 principales alternatives, mais je n'ai pas le temps de l'exécuter plusieurs fois pour obtenir les moyens et consigner les temps pour plusieurs entrées ... Alors, voici le code si quelqu'un veut l'essayer:

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

Il faut 2 arguments, le premier est le nombre d'itérations (chaque fonction est exécutée avec des temps de répétition arg de 1..n) et le second est la chaîne à répéter.

Jusqu'ici, une inspection rapide des temps passés avec différentes entrées laisse le classement ressembler à ceci (mieux en pire):

  1. Itératif StringBuilder ajoute (1x).
  2. Invocations récursives de log2 de concaténation (~ 3x).
  3. Invocations linéaires de concaténation récursive (~ 30x).
  4. Concaténation itérative linéaire (~ 45x).

Je n'aurais jamais deviné que la fonction récursive était plus rapide que la boucle for: -o

Amusez-vous (Ctional xD).

32
fortran

Cela contient moins de caractères que votre 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();
}
20
Pyrolistical

sur la base de fortran answer , il s'agit d'une version récursive qui utilise 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();
}
9
dfa

Je voulais une fonction pour créer une liste de points d'interrogation séparés par des virgules à des fins JDBC, et j'ai trouvé cet article. J'ai donc décidé de prendre deux variantes et de voir laquelle fonctionnait le mieux. Après 1 million d'itérations, StringBuilder a pris 2 secondes (fun1), et la version supposée plus optimale (fun2) a pris 30 secondes. Quel est l'intérêt d'être à nouveau cryptique?

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

Solution POO

Presque chaque réponse propose une fonction statique comme solution, mais en pensant orienté objet (pour des raisons de réutilisabilité et de clarté), j'ai proposé une solution via une délégation via l'interface CharSequence (qui ouvre également la possibilité d'utilisation sur des classes CharSequence mutables).

La classe suivante peut être utilisée avec ou sans Separator-String/CharSequence et chaque appel à "toString ()" crée la chaîne répétée finale. Les entrées/séparateurs ne sont pas seulement limités à String-Class, mais peuvent être toutes les classes qui implémentent CharSequence (par exemple, StringBuilder, StringBuffer, etc.)!

Code source:

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

Exemple d'utilisation:

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

Exemple de sortie:

Repeat=2    Separator=<=    Input=123   Length=3
CharSeq:    Length=8    Val=123<=123
String :    Length=8    Val=123<=123
123ff<=123ff
123ff<====>123ff
7

utiliser Dollar est simple:

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

PS: répéter fonctionne également pour les tableaux, les listes, les ensembles, etc.

7
dfa

en utilisant uniquement les classes JRE ( System.arraycopy ) et en essayant de réduire le nombre d'objets temporaires que vous pouvez écrire, par exemple:

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

et sans boucles, vous pouvez essayer avec:

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

utiliser Collections est encore plus court:

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

cependant, j'aime toujours la première version.

6
dfa

Si la rapidité vous préoccupe, utilisez au minimum la copie en mémoire. Il est donc nécessaire de travailler avec des tableaux de caractères.

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

Pour tester la vitesse, une méthode optimale similaire utilisant StirngBuilder est la suivante:

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

et le code pour le tester:

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

}

Et voici le résultat de mon système:

elapsed repeatString: 6006571
elapsed repeatStringSB: 9064937

Notez que le test de la boucle consiste à lancer JIT et à obtenir des résultats optimaux.

5
Panayotis

Pas le plus court, mais ((je pense)) le moyen le plus rapide consiste à utiliser le 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();
  }
5
Simon

pour des raisons de lisibilité et de portabilité:

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

Si les performances vous inquiètent, utilisez simplement un StringBuilder à l'intérieur de la boucle et faites un .toString () à la sortie de la boucle. Heck, écrivez votre propre classe Util et réutilisez-la. 5 lignes de code max.

3
WolfmanDragon

Essayez ceci:

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

En utilisant la récursivité, vous pouvez effectuer les opérations suivantes (en utilisant des opérateurs ternaires, une ligne maximum):

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

Je sais, c'est moche et probablement pas efficace, mais c'est une ligne!

2
HyperNeutrino

Boucle simple

public static String repeat(String string, int times) {
    StringBuilder out = new StringBuilder();
    while (times-- > 0) {
        out.append(string);
    }
    return out.toString();
}
2
Codler
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);
}
2
falcon

J'aime beaucoup cette question. Il y a beaucoup de connaissances et de styles. Donc je ne peux pas le laisser sans montrer mon 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);
    }
}

Aimez-vous?

2
Daniel De León

Malgré votre désir de ne pas utiliser de boucles, je pense que vous devriez utiliser une boucle.

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

Vos raisons de ne pas utiliser une boucle for ne sont pas bonnes. En réponse à vos critiques:

  1. Quelle que soit la solution que vous utilisez, elle sera certainement plus longue que cela. L'utilisation d'une fonction prédéfinie ne la place que sous plusieurs couvertures.
  2. Quelqu'un lisant votre code devra comprendre ce que vous faites dans cette boucle non-pour-boucle. Etant donné qu'une boucle for est la manière idiomatique de le faire, il serait beaucoup plus facile de déterminer si vous l'avez fait avec une boucle for.
  3. Oui, quelqu'un pourrait ajouter quelque chose d'intelligent, mais en évitant une boucle for , vous êtes faites quelque chose d'intelligent . C'est comme se tirer une balle dans le pied intentionnellement pour éviter de se tirer dans le pied par accident.
  4. Les erreurs off-by-one sont également faciles à détecter avec un seul test. Étant donné que vous devriez tester votre code, une erreur particulière devrait être facile à corriger et à détecter. Et il est à noter que le code ci-dessus ne contient pas d'erreur erronée. Les boucles sont également faciles à obtenir.
  5. Donc, ne réutilisez pas les variables. Ce n'est pas la faute de la boucle for.
  6. Encore une fois, quelle que soit la solution que vous utilisez. Et comme je l'ai noté auparavant; un chasseur de bogues s’attendra probablement à ce que vous le fassiez avec une boucle for, de sorte qu’il sera plus facile de le trouver si vous utilisez une boucle for.
1
Imagist

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

elle n'a même pas besoin d'être aussi grosse, elle peut être transformée en ceci et peut être copiée et collée dans une classe d'utilitaires de votre projet.

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

Donc e5, je pense que le meilleur moyen de faire cela serait simplement d’utiliser le code mentionné ci-dessus, ou l’une des réponses données ici. mais commons lang est tout simplement trop gros s'il s'agit d'un petit projet

0
alexmherrmann
public static String rep(int a,String k)

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

Vous pouvez utiliser cette méthode récursive pour votre objectif souhaité.

0
Syed Salman Hassan

J'ai créé une méthode récursive qui fait la même chose que vous voulez .. n'hésitez pas à l'utiliser ...

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

j'ai la même réponse sur Puis-je multiplier des chaînes dans Java pour répéter des séquences?

0
niczm25