Un moyen simple de répéter une chaîne en Java

598

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

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

repeated.equals("abcabcabc");

Relative à:

répéter la chaîne javascript Créer NSString en répétant une autre chaîne un certain nombre de fois

Édité

J'essaye d'éviter les boucles quand elles ne sont pas complètement nécessaires car:

  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 for. Même s'il est commenté et a des noms de variables significatifs, ils doivent toujours s'assurer qu'il ne fait rien de «intelligent».

  3. Les programmeurs adorent mettre des choses intelligentes dans les boucles, même si je l'écris pour "ne faire que ce qu'il est censé faire", cela n'empêche pas quelqu'un de venir et d'ajouter quelques "correctifs" supplémentaires.

  4. Ils sont très souvent faciles à se tromper. Pour les boucles impliquant des index, les bugs ont tendance à se produire.

  5. Pour les boucles, réutilisez souvent les mêmes variables, ce qui augmente les chances de trouver des bogues de portée très difficiles.

  6. Pour les boucles, augmentez le nombre d'endroits qu'un chasseur de bogues doit rechercher.

Ethan Heilman
la source
35
Je comprends que les boucles for peuvent causer de vrais problèmes. Mais vous ne devriez pas essayer d'éviter les boucles "à tout prix", car si cela vous coûte la lisibilité, la maintenabilité et la vitesse, vous êtes contre-productif. C'est l'un de ces cas.
Imagist
9
"Ils ajoutent au nombre de lignes de code même s'ils sont cachés dans une autre fonction" ... wow, wow. Big-O, not LoC
Pyrolistical
7
@imagist J'évite les boucles dans les situations où cela me coûte la lisibilité et la maintenabilité. Je considère la vitesse comme le problème le moins important ici (un non-problème en fait). Je pense que les boucles for sont surutilisées et j'essaie d'apprendre à n'utiliser les boucles for que lorsqu'elles sont nécessaires et non comme solution par défaut.
Ethan Heilman
3
@Pyrolistique Je ne revendique pas la performance ou les avantages asymptotiques. Plutôt dire qu'en écrivant moins de code, et en utilisant les fonctions de bibliothèque plutôt que de réinventer la roue, je réduis la surface de bug (Lines of Code) tout en augmentant la lisibilité. Les deux bonnes choses, je suis sûr que vous serez d'accord.
Ethan Heilman
4
@ e5; désolé pour avoir posté des années plus tard. Je trouve cette question si appropriée. S'ils sont insérés dans une méthode, les arguments doivent être testés (fois> = 0), les erreurs levées etc. Cela ajoute de la robustesse mais aussi des lignes de code à lire. La répétition d'une chaîne est quelque chose d'univoque. Celui qui lit le code sait exactement ce que fait une chaîne. La répétition, même sans ligne de commentaire ni javadoc. Si nous utilisons une bibliothèque stable, il est raisonnable de penser qu'une fonction aussi simple n'a pas de bogues, ENCORE introduit une forme de vérification de «robustesse» dont nous devons même nous inquiéter. Si je pouvais demander 10 améliorations, ce (genre de) choses en serait une.
AgostinoX

Réponses:

230

String::repeat

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

Nouveau dans Java 11 est la méthode String::repeatqui 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
 */ 
Nicolai
la source
2
@Nicolai code source pour cela, juste au cas où quelqu'un se soucierait de hg.openjdk.java.net/jdk/jdk/file/fc16b5f193c7/src/java.base/…
Eugene
7
je n'ai même pas encore vu java 9 dans la rue (et ne le serai pas depuis longtemps ..) - et 11 est apparemment prêt à être expédié ..
javadba
1
Probablement évident, mais vous pouvez aussi appeler cette méthode sur un littéral de chaîne:"abc".repeat(3)
Boann
895

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

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

nest le nombre de répétitions de la chaîne et sla chaîne à répéter.

Aucune importation ou bibliothèque nécessaire.

user102008
la source
22
Je ne pense pas que ce soit obscurci du tout. Les types primitifs ( char[], dans ce cas) sont instanciés avec des valeurs nulles, puis un Stringest créé à partir de la char[], et les valeurs nulles sont replaced()avec le caractère que vous souhaitez danss
Amarok
32
Bien que cela soit très intelligent (+1), je pense que cela prouve à peu près le point que pour les boucles, le code est souvent plus clair
Richard Tingle
75
Pour ceux qui se plaignent d'obscurcissement, la lisibilité dépend de l'alphabétisation. Cela est parfaitement clair dans ce qu'il fait et éducatif pour ceux qui ne le voient pas tout de suite. C'est ce que vous obtenez avec Java d'ailleurs.
dansalmo
11
Pour de meilleures performances, ...replace('\0', str)utilisez plutôt la version String.
user686249
11
@ user686249: Il n'y en a que replace(char oldChar, char newChar)et replace(CharSequence target, CharSequence replacement)donc je ne vois pas comment cela pourrait fonctionner
user102008
334

Si vous utilisez Java <= 7 , c'est aussi "concis" que possible:

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

En Java 8 et supérieur, il existe un moyen plus lisible:

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

Enfin, pour Java 11 et supérieur, il existe une nouvelle repeat​(int count)méthode spécialement pour cela ( lien )

"abc".repeat(12);

Alternativement, si votre projet utilise des bibliothèques java, il y a plus d'options.

Pour Apache Commons :

StringUtils.repeat("abc", 12);

Pour Google Guava :

Strings.repeat("abc", 12);
Caner
la source
4
Le premier provoque une exception quand nest zéro.
saka1029
L'exemple java 8 ne compile pas -> Non-concordance de type: impossible de convertir de List <Character> en CharSequence
Arigion
6
@Arigion sdoit être String, pas unChar
Caner
@Caner Merci. Mon mauvais, je m'excuse. Apparemment, j'étais trop fatigué hier. Désolé pour le vote négatif. Je supprimerai le downvote dès que possible (il est bloqué jusqu'à ce que la question soit modifiée)
Arigion
@Arigion pas de problème, il est clair maintenant que s est une chaîne
Caner
312

Commons Lang StringUtils.repeat ()

Usage:

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

repeated.equals("abcabcabc");
ChssPly76
la source
99
utiliser une dépendance à une méthode pour des raisons de simplicité à long terme peut entraîner un enfer
dfa
81
Bien sûr, sauf que c'est le langage commun. Je ne pense pas avoir jamais vu un projet de plus de 5000 LOCS qui n'avait pas de langue commune.
Ethan Heilman
11
Commons Lang est open source - téléchargez-le et jetez un œil. Bien sûr, il a une boucle à l'intérieur, mais ce n'est pas aussi simple. Beaucoup d'efforts ont été consacrés au profilage et à l'optimisation de cette mise en œuvre.
ChssPly76
28
Je n'évite pas les boucles pour des raisons de performances (lisez mes raisons dans la question). Quand quelqu'un voit StringUtils.repeat, il sait ce que je fais. Ils n'ont pas à s'inquiéter que j'ai essayé d'écrire ma propre version de Repeat et que j'ai fait une erreur. C'est une unité cognitive atomique!
Ethan Heilman
7
@ Thorbjørn Ravn Andersen - cela peut être BEAUCOUP plus intéressant si les choses continuent à être sorties de leur contexte
ChssPly76
140

Java 8 String.joinfournit une manière ordonnée de le faire en conjonction avec Collections.nCopies:

// say hello 100 times
System.out.println(String.join("", Collections.nCopies(100, "hello")));
Boann
la source
7
Merci! Pour android TextUtils.join () peut être utilisé à la place de String.join ()
MinaHany
2
Merci pour cette réponse. Cela semble être le moyen le plus propre sans utiliser de méthode d'utilitaire API externe! très bon!!
Andreas M. Oberheim
2
La bonne chose à propos de cette méthode est qu'avec la jointure, vous pouvez fournir un caractère de séparation qui s'avère très pratique si vous construisez, par exemple, une liste CSV. Avec toutes les autres méthodes, vous disposez d'un caractère de jonction de fin qui doit être supprimé dans une opération distincte.
DroidOS
102

Voici un moyen de le faire en utilisant uniquement des fonctions String standard et aucune boucle explicite:

// create a string made up of  n  copies of  s
repeated = String.format(String.format("%%%ds", n), " ").replace(" ",s);
IJ Kennedy
la source
5
Incroyable :-) Bien que méfiez-vous de n devenir zéro…!
Yang Meyer
4
@Vijay Dev & fortran: Non, il voulait dire replace(). Dans Java 1.5+, il existe une version surchargée replace()qui prend deux CharSequences (qui incluent Strings): download.oracle.com/javase/1.5.0/docs/api/java/lang/…
user102008
79
Aie. C'est moche.
Karel Bílek
8
@mzuba disons n=3: il formate d'abord une chaîne pour ressembler à quelque chose %03d( %%c'est pour échapper au signe de pourcentage), qui est le code de mise en forme pour ajouter 3 zéros de remplissage, puis formate 0avec cela, conduisant à 000, et enfin remplace chaque0 par les chaînes
fortran
15
Vous pouvez rendre la solution moins moche et plus facile à comprendre: String.format ("% 0" + n + "d", 0) .replace ("0", s)
Artur
87

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

Strings.repeat("-", 60);
Jack
la source
2
... et obtenez 3 Mo de nouvelles dépendances.
MonoThreaded
6
@MonoThreaded Je pensais que cela irait sans dire, mais n'incluez pas la goyave juste pour faire une répétition de chaîne. Ma réponse était que si vous utilisez déjà de la goyave de toute façon, c'est comme ça que vous le feriez.
Jack
53

Avec , 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 utilitaire simple si nécessaire:

public static String repeat(String str, int times) {
   return Stream.generate(() -> str).limit(times).collect(joining());
}
Alexis C.
la source
6
... ou return IntStream.range(0, times).mapToObj(i -> str).collect(joining());qui parallèle mieux
Alexis C.
32

Vous voulez donc é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'a pas de boucles :-p)

Vous le voulez plus simple et plus joli? utilisez jython:

s * 3

Edit : optimisons-le 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 un benchmark 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 tracer les temps pour plusieurs entrées ... Alors voici le code si quelqu'un veut pour 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 prend 2 arguments, le premier est le nombre d'itérations (chaque fonction s'exécute avec des temps de répétition arg de 1..n) et le second est la chaîne à répéter.

Jusqu'à présent, une inspection rapide des temps d'exécution avec différentes entrées laisse le classement quelque chose comme ça (de mieux en pire):

  1. Ajout de StringBuilder itératif (1x).
  2. Appels récursifs de concaténation log2 (~ 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 forboucle: -o

Amusez-vous (ctional xD).

fortran
la source
1
+1 pour la récursivité et étant évidemment un hacker lisp. Je ne pense pas que ce soit si inefficace non plus, la concaténation de chaînes n'est pas le crime de guerre qu'elle était autrefois, car + n'est vraiment qu'un stringBuilder UTH. Voir stackoverflow.com/questions/47605/java-string-concatenation et schneide.wordpress.com/2009/02/23/… . Je me demande combien toutes ces piles poussent et sautent du coût de la récursivité, ou si le hotspot s'en occupe. J'aimerais vraiment avoir le temps libre pour le comparer. Quelqu'un d'autre peut-être?
Ethan Heilman
@ e5: fortran a raison; cette solution pourrait être rendue plus efficace. Cette implémentation créera inutilement un nouveau StringBuilder (et une nouvelle chaîne) pour chaque récursivité. C'est quand même une bonne solution.
voler
3
@ e5 J'aurais aimé être un hacker Lisp xD ... Si je l'avais été, j'aurais utilisé une fonction récursive de queue :-p
fortran
1
Les microbenchmarks ne fonctionnent pas bien en Java. Essayer de mesurer la vitesse de vos implémentations comme ça n'est pas bon.
ceklock
@tecnotron Je sais, mais ils valent mieux que rien ... Et la seule "surprise" était la légère différence entre la concaténation de boucle naïve et la récursivité linéaire.
fortran
20

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();
}
Pyrolistique
la source
4
Il contient plus de caractères que ma réponse StringUtils.repeat (str, n).
Ethan Heilman
8
À moins que vous n'utilisiez déjà Apache Commons, cette réponse est beaucoup moins compliquée - pas de téléchargement d'une autre bibliothèque, y compris dans votre chemin de classe, en vous assurant que sa licence est compatible avec la vôtre, etc.
Paul Tomblin
7
Veuillez ne jamais retourner null - dans ce cas, renvoyez une chaîne vide, ce qui vous permet de toujours utiliser la valeur renvoyée sans contrôle. Sinon, ce que je recommanderais d'utiliser l'affiche.
Thorbjørn Ravn Andersen
7
Eh bien, il existe trois façons de gérer si s est nul. 1. Passez l'erreur (return null), 2. Cachez l'erreur (return ""), 3. Lancez un NPE. Masquer l'erreur et lancer un NPE ne sont pas cool, j'ai donc transmis l'erreur.
Pyrolistical
1
@EthanHeilman ajoutez la valeur de 2 Mo commons-lang3.3.1-sourceset vous n'êtes plus aussi bon;) Mais si quelqu'un l'a déjà commons-lang, j'appuie votre réponse.
TWiStErRob
9

basé sur la réponse de fortran , ceci est 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();
}
dfa
la source
1
la boucle plutôt que la récursivité réduirait le nombre de trames de pile pour un grand nombre de répétitions.
La-comadreja
7

utiliser Dollar est aussi simple que de taper:

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

PS: la répétition fonctionne également pour le tableau, la liste, le jeu, etc.

dfa
la source
3
la méthode assertThat () est-elle vraiment nécessaire?
ceklock
7

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é ce message. J'ai donc décidé de prendre deux variantes et de voir laquelle fonctionnait mieux. Après 1 million d'itérations, le StringBuilder, une variété de jardin, a pris 2 secondes (fun1), et la version cryptique 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);
}
Bob
la source
3
Il est logique que la seconde prenne beaucoup plus de temps. Il effectue une recherche de chaîne, puis modifie la chaîne caractère par caractère.
Ethan Heilman
7

Solution POO

Presque toutes les réponses proposent une fonction statique comme solution, mais en pensant orienté objet (à des fins de réutilisabilité et de clarté), j'ai trouvé une solution via la délégation via l'interface CharSequence (qui ouvre également l'utilisabilité sur les 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
Maciej Schuttkowski
la source
4
J'ai rarement vu des choses dégueulasses: /
Ven
6

en utilisant uniquement des classes JRE ( System.arraycopy ) et en essayant de minimiser le nombre d'objets temporaires, vous pouvez écrire quelque chose comme:

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

ÉDITER

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

l'utilisation des collections est encore plus courte:

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.

dfa
la source
6
-1: trop malin de moitié. Si votre objectif est de vous rendre le code lisible ou efficace, ces "solutions" ne sont pas une bonne idée. 'repeat' pourrait simplement être réécrit à l'aide d'un StringBuilder (définissant la capacité initiale). Et 'repeat2' / 'repeat3' sont vraiment inefficaces et dépendent de la syntaxe non spécifiée de la chaîne produite par String []. ToString ().
Stephen C
@Thorb: absolument, avec ce code, vous ne pouvez pas utiliser "metacharacter", [],
dfa
@Stephen: la question a été modifiée pour ne demander explicitement aucune boucle. Une réponse basée sur StringBuilder a déjà été fournie, j'ai donc évité d'en poster un doublon
dfa
@Stephan: Je ne peux pas comprendre le downvote. Ma réponse modifiée est sans boucle, comme requis. Il n'y a aucune demande d'efficacité. Je pense que cette question n'est qu'un effort intellectuel pour produire une concaténation sans boucle.
dfa
@Stephan: La chaîne produite via Collection.toString (et Arrays.toString) est clairement spécifiée dans AbstractCollection.toString: "La représentation sous forme de chaîne se compose d'une liste des éléments de la collection dans l'ordre où ils sont renvoyés par son itérateur, entre crochets ( "[]"). Les éléments adjacents sont séparés par les caractères "," (virgule et espace). "
dfa
6

Pas le plus court, mais (je pense) le moyen le plus rapide est d'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();
  }
Simon
la source
5

Si la vitesse est votre préoccupation, vous devez utiliser le moins de copie de mémoire possible. 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 comme ceci:

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 les résultats de l'exécution de mon système:

elapsed repeatString: 6006571
elapsed repeatStringSB: 9064937

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

Panayotis
la source
4

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);
}
Martin Zeitler
la source
3

Si vous êtes préoccupé par les performances, utilisez simplement un StringBuilder dans 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.

WolfmanDragon
la source
2

J'apprécie vraiment cette question. Il y a beaucoup de connaissances et de styles. Je ne peux donc pas le quitter 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);
    }
}

Aimes-tu?

Daniel De León
la source
1
Dans mon test le plus extrême, je produis une longueur de répétition de chaîne de 1,700,000,000 (1,7 gigas), en utilisant -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);
}
faucon
la source
2

Boucle simple

public static String repeat(String string, int times) {
    StringBuilder out = new StringBuilder();
    while (times-- > 0) {
        out.append(string);
    }
    return out.toString();
}
Codler
la source
2
passer timesau constructeur StringBuilder.
Behrouz.M
2

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

la source
2

En utilisant la récursivité, vous pouvez faire ce qui suit (en utilisant des opérateurs ternaires, une ligne max):

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 seule ligne!

HyperNeutrino
la source
C'est l'approche que j'adopterais, mais pourquoi faire plus de vérifications que nécessaire? numéro de retour> 0? chaîne + répétition (chaîne, numéro-1): "";
Fering
Oh, semble niczm25 répondu avec elle ci
Fering
@Fering raison principale pour que cette voie soit toujours la moyenne O (log N) plutôt que O (N). Un peu plus d'optimisation que l'autre, bien que toujours mauvais.
HyperNeutrino
2

une solution simple en ligne:
nécessite Java 8

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

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 de boucle for ne sont pas bonnes. En réponse à vos critiques:

  1. Quelle que soit la solution que vous utiliserez, elle sera certainement plus longue que cela. L'utilisation d'une fonction pré-construite ne fait que la placer sous plus de couvertures.
  2. Quelqu'un qui lit votre code devra comprendre ce que vous faites dans cette boucle sans boucle. Étant donné qu'une boucle for est la façon 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 que vous êtes en train de faire 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 ponctuelles sont également incroyablement faciles à détecter avec un seul test. Étant donné que vous devriez tester votre code, une erreur hors-ligne devrait être facile à corriger et à détecter. Et cela vaut la peine de le noter: le code ci-dessus ne contient pas d'erreur off-by-one. Car les boucles sont tout aussi faciles à réaliser.
  5. Ne réutilisez donc pas les variables. Ce n'est pas la faute de la boucle for.
  6. Encore une fois, il en va de même de la solution que vous utilisez. Et comme je l'ai noté précédemment; un chasseur de bogues s'attendra probablement à ce que vous le fassiez avec une boucle for, il aura donc plus de facilité à le trouver si vous utilisez une boucle for.
Imagist
la source
3
-1. Voici deux exercices pour vous: a) exécutez votre code avec repetitions = -5. b) téléchargez Commons Lang et exécutez repeatString('a', 1000)un million de fois en boucle; faites de même avec votre code; comparer les temps. Pour un crédit supplémentaire, faites de même avec repeatString('ab', 1000).
ChssPly76
2
Êtes-vous en train de dire que votre code est alors plus lisible StringUtils.repeat("ab",1000)? Parce que c'est ma réponse que vous avez rejetée. Il fonctionne également mieux et n'a pas de bugs.
ChssPly76
2
Lisez la 2e phrase de la question que vous citez. "J'essaie d'éviter les boucles à tout prix parce que" a été ajouté à la question en guise de clarification en réponse à la réponse d'Andrew Hare après ma réponse - pas que cela soit important parce que si la position que vous prenez est "la réponse est mauvaise si la boucle est utilisé partout "il n'y a pas de réponse à la question OP. Même les solutions de dfa - aussi inventives soient-elles - sont utilisées pour les boucles à l'intérieur. "l'enfer de pot" a été répondu ci-dessus; commons lang est de toute façon utilisé dans toutes les applications de taille décente et n'ajoute donc pas de nouvelle dépendance.
ChssPly76
2
@ ChssPly76 à ce stade, je suis presque sûr que l'imagiste est à la traîne. J'ai vraiment du mal à voir comment n'importe qui pourrait lire ce que j'ai écrit et penser sérieusement aux réponses tapées ci-dessus.
Ethan Heilman
1
@ ChssPly76 mes réponses n'ont pas de boucles du tout :-p
fortran
1

Si vous ne connaissez que la longueur de la chaîne de sortie (et qu'elle peut ne pas être divisible par la longueur de la chaîne d'entrée), utilisez cette méthode:

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

Démo d'utilisation:

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

Ne pas utiliser avec vide set length> 0, car il est impossible d'obtenir le résultat souhaité dans ce cas.

John McClane
la source
0

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

il n'a même pas besoin d'être aussi gros, peut être transformé en ceci, et peut être copié et collé dans une classe utilitaire dans 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 la meilleure façon de faire serait d'utiliser simplement le code mentionné ci-dessus, ou l'une des réponses ici. mais lang communes est tout simplement trop grand si c'est un petit projet

alexmherrmann
la source
Je ne pense pas qu'il y ait beaucoup d'autre que vous puissiez faire ... excet peut-être un AOT !!
alexmherrmann
0

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

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 les chaînes en java pour répéter les séquences?

niczm25
la source
Réallocation inutile des chaînes et surcharge de récursivité ... mauvaise, mauvaise, pas bonne.
Alexander - Rétablir Monica
1
Ce sera lent. Non recommandé! Utilisez StringBuilderplutôt.
Svetlin Nakov du
0
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é.

Syed Salman Hassan
la source