Einfache Möglichkeit, einen String in Java zu wiederholen

597

Ich suche nach einer einfachen Commons-Methode oder einem einfachen Commons-Operator, mit dem ich einige Zeichenfolgen n- mal wiederholen kann . Ich weiß, dass ich dies mit einer for-Schleife schreiben könnte, aber ich möchte for-Schleifen bei Bedarf vermeiden, und irgendwo sollte eine einfache direkte Methode existieren.

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

repeated.equals("abcabcabc");

Bezüglich:

String-Javascript wiederholen Erstellen Sie NSString, indem Sie einen anderen String eine bestimmte Anzahl von Malen wiederholen

Bearbeitet

Ich versuche, for-Schleifen zu vermeiden, wenn sie nicht unbedingt erforderlich sind, weil:

  1. Sie erhöhen die Anzahl der Codezeilen, selbst wenn sie in einer anderen Funktion versteckt sind.

  2. Jemand, der meinen Code liest, muss herausfinden, was ich in dieser for-Schleife mache. Selbst wenn es kommentiert ist und aussagekräftige Variablennamen hat, müssen sie dennoch sicherstellen, dass es nichts "Kluges" tut.

  3. Programmierer lieben es, clevere Dinge für Schleifen einzufügen, auch wenn ich sie schreibe, um "nur das zu tun, was sie tun sollen", was nicht ausschließt, dass jemand mitkommt und zusätzliche clevere "Korrekturen" hinzufügt.

  4. Sie sind sehr oft leicht zu verwechseln. Bei Schleifen, an denen Indizes beteiligt sind, wird dies in der Regel durch einen Fehler ausgelöst.

  5. For-Schleifen verwenden häufig dieselben Variablen wieder, was die Wahrscheinlichkeit erhöht, dass Scoping-Fehler wirklich schwer zu finden sind.

  6. Für Schleifen erhöhen Sie die Anzahl der Stellen, nach denen ein Insektenjäger suchen muss.

Ethan Heilman
quelle
35
Ich verstehe, dass for-Schleifen einige echte Probleme verursachen können. Sie sollten jedoch nicht versuchen, Schleifen "um jeden Preis" zu vermeiden, da Sie kontraproduktiv sind, wenn dies Lesbarkeit, Wartbarkeit und Geschwindigkeit kostet. Dies ist einer dieser Fälle.
Imagist
9
"Sie erhöhen die Anzahl der Codezeilen, selbst wenn sie in einer anderen Funktion versteckt sind" ... wow, nur wow. Big-O, nicht LoC
Pyrolistical
7
@imagist Ich vermeide Schleifen in Situationen, in denen es mich Lesbarkeit und Wartbarkeit kostet. Ich betrachte Geschwindigkeit hier als das am wenigsten wichtige Thema (eigentlich kein Thema). Ich denke, dass for-Schleifen überbeansprucht werden und ich versuche zu lernen, for-Schleifen nur dann zu verwenden, wenn sie notwendig sind und nicht als Standardlösung.
Ethan Heilman
3
@Pyrolistical Ich beanspruche keine Leistung oder asymptotischen Vorteile. Anstatt weniger Code zu schreiben und Bibliotheksfunktionen zu verwenden, anstatt das Rad neu zu erfinden, reduziere ich die Fehleroberfläche (Codezeilen) und erhöhe gleichzeitig die Lesbarkeit. Ich bin mir sicher, dass Sie beiden guten Dingen zustimmen werden.
Ethan Heilman
4
@ e5; Entschuldigung für die Veröffentlichung Jahre später. Ich finde diese Frage so angemessen. Wenn sie in eine Methode eingefügt werden, sollten Argumente getestet werden (Zeiten> = 0), Fehler ausgelöst werden usw. Dies erhöht die Robustheit, aber auch die zu lesenden Codezeilen. Das Wiederholen einer Zeichenfolge ist eindeutig. Wer den Code liest, weiß genau, was eine Zeichenfolge tut. Wiederholen, auch ohne Kommentarzeile oder Javadoc. Wenn wir eine stabile Bibliothek verwenden, ist es vernünftig zu glauben, dass eine so einfache Funktion keine Fehler aufweist. NOCH führt eine Form der "Robustheits" -Prüfung ein, über die wir uns sogar Sorgen machen müssen. Wenn ich 10 Verbesserungen verlangen könnte, wäre dies eine Art von Dingen.
AgostinoX

Antworten:

229

String::repeat

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

Neu in Java 11 ist die Methode String::repeat, die genau das tut, wonach Sie gefragt haben:

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

Sein Javadoc sagt:

/**
 * 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
quelle
2
@Nicolai Quellcode dafür, nur für den Fall, dass sich jemand um hg.openjdk.java.net/jdk/jdk/file/fc16b5f193c7/src/java.base/…
Eugene
7
Ich habe Java 9 noch nicht einmal auf der Straße gesehen (und werde es noch lange nicht
tun
1
Wahrscheinlich offensichtlich, aber Sie können diese Methode auch für ein String-Literal aufrufen:"abc".repeat(3)
Boann
895

Hier ist die kürzeste Version (Java 1.5+ erforderlich):

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

Wo nist die Häufigkeit, mit der Sie sdie Zeichenfolge wiederholen möchten, und wo wird die Zeichenfolge wiederholt?

Keine Importe oder Bibliotheken erforderlich.

user102008
quelle
22
Ich denke nicht, dass es überhaupt verschleiert ist. Primitive Typen ( char[]in diesem Fall) werden mit Nullen instanziiert, dann Stringwird a aus dem erstellt char[], und die Nullen haben das gewünschte replaced()Zeichens
Amarok,
32
Dies ist zwar sehr klug (+1), aber ich denke, es beweist ziemlich genau, dass for-Schleifen oft zu klarerem Code führen
Richard Tingle
75
Für diejenigen, die sich über Verschleierung beschweren, hängt die Lesbarkeit von der Alphabetisierung ab. Dies ist vollkommen klar in dem, was es tut und lehrreich für diejenigen, die es möglicherweise nicht sofort sehen. Das bekommen Sie übrigens mit Java.
Dansalmo
11
Für eine bessere Leistung ...replace('\0', str)sollte anstelle der String-Version verwendet werden.
user686249
11
@ user686249: Es gibt nur replace(char oldChar, char newChar)und replace(CharSequence target, CharSequence replacement)so sehe ich nicht, wie das funktionieren könnte
user102008
334

Wenn Sie Java <= 7 verwenden , ist dies so "prägnant" wie es nur geht:

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

In Java 8 und höher gibt es eine besser lesbare Möglichkeit:

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

Schließlich gibt es für Java 11 und höher eine neue repeat​(int count)Methode speziell dafür ( Link )

"abc".repeat(12);

Wenn Ihr Projekt Java-Bibliotheken verwendet, gibt es alternativ mehr Optionen.

Für Apache Commons :

StringUtils.repeat("abc", 12);

Für Google Guava :

Strings.repeat("abc", 12);
Caner
quelle
4
Ersteres verursacht eine Ausnahme, wenn nNull ist.
saka1029
Das Java 8-Beispiel wird nicht kompiliert -> Typkonflikt: Konvertiert nicht von List <Character> nach CharSequence
Arigion
6
@Arigion smuss String sein, kein aChar
Caner
@Caner Danke. Mein schlechtes, ich entschuldige mich. Anscheinend war ich gestern zu müde. Entschuldigung für das Downvoting. Ich werde das Downvote entfernen, sobald ich konnte (es ist blockiert, bis die Frage bearbeitet wird)
Arigion
@Arigion kein Problem, machte jetzt klar, dass s eine Zeichenfolge ist
Caner
312

Commons Lang StringUtils.repeat ()

Verwendungszweck:

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

repeated.equals("abcabcabc");
ChssPly76
quelle
99
Die Verwendung einer Ein-Methoden-Abhängigkeit kann der Einfachheit halber auf lange Sicht zu einer Hölle führen
dfa
81
Sicher, außer es ist Commons Lang. Ich glaube nicht, dass ich jemals ein Projekt über 5000 LOCS gesehen habe, das keine Commons Lang hatte.
Ethan Heilman
11
Commons Lang ist Open Source - laden Sie es herunter und schauen Sie es sich an. Natürlich hat es eine Schleife im Inneren, aber es ist nicht ganz so einfach. Es wurden große Anstrengungen unternommen, um diese Implementierung zu profilieren und zu optimieren.
ChssPly76
28
Ich vermeide Schleifen aus Leistungsgründen nicht (lesen Sie meine Gründe in der Frage). Wenn jemand StringUtils.repeat sieht, weiß er, was ich tue. Sie müssen sich keine Sorgen machen, dass ich versucht habe, meine eigene Version von repeat zu schreiben, und einen Fehler gemacht habe. Es ist eine atomare kognitive Einheit!
Ethan Heilman
7
@ Thorbjørn Ravn Andersen - es kann viel interessanter werden, wenn die Dinge immer wieder aus dem Zusammenhang
gerissen werden
140

Java 8 String.joinbietet eine ordentliche Möglichkeit, dies in Verbindung mit Collections.nCopies:

// say hello 100 times
System.out.println(String.join("", Collections.nCopies(100, "hello")));
Boann
quelle
7
Vielen Dank! Für Android TextUtils.join () kann anstelle von String.join () verwendet werden
MinaHany
2
Vielen Dank für diese Antwort. Es scheint der sauberste Weg zu sein, ohne eine externe API oder Dienstprogrammmethode zu verwenden! sehr gut!!
Andreas M. Oberheim
2
Das Schöne an dieser Methode ist, dass Sie mit join ein Trennzeichen bereitstellen können, das sich als sehr praktisch herausstellt, wenn Sie beispielsweise eine CSV-Liste erstellen. Bei allen anderen Methoden haben Sie ein abschließendes Verbindungszeichen, das in einem separaten Vorgang entfernt werden muss.
DroidOS
102

Hier ist eine Möglichkeit, dies nur mit Standard-String-Funktionen und ohne explizite Schleifen zu tun:

// create a string made up of  n  copies of  s
repeated = String.format(String.format("%%%ds", n), " ").replace(" ",s);
IJ Kennedy
quelle
5
Erstaunlich :-) Obwohl du aufpasst, dass n nicht Null wird…!
Yang Meyer
4
@ Vijay Dev & fortran: Nein, er meinte replace(). In Java 1.5+ gibt es eine überladene Version replace(), die zwei Sekunden benötigt CharSequence(einschließlich Strings): download.oracle.com/javase/1.5.0/docs/api/java/lang/…
user102008
79
Autsch. Das ist hässlich.
Karel Bílek
8
@mzuba sagen wir n=3: es erste Formate einen String zu sehen etwas wie %03d( %%ist das Prozentzeichen zu entkommen), die die Formatierung von Code 3 padding Nullen hinzufügen, dann Formate 0mit dem, was zu 000und ersetzt schließlich jeweils 0mit den Saiten
fortran
15
Sie können die Lösung weniger hässlich und verständlicher machen: String.format ("% 0" + n + "d", 0) .replace ("0", s)
Artur
87

Wenn Sie wie ich sind und Google Guava und nicht Apache Commons verwenden möchten. Sie können die Wiederholungsmethode in der Guava Strings-Klasse verwenden.

Strings.repeat("-", 60);
Jack
quelle
2
... und 3 MB neue Abhängigkeiten erhalten.
MonoThreaded
6
@MonoThreaded Ich dachte, es wäre selbstverständlich, aber füge keine Guave hinzu, nur um eine Zeichenfolgenwiederholung durchzuführen. Meine Antwort war, wenn Sie ohnehin schon Guave verwenden, dann würden Sie es so machen.
Jack
53

Mit können Sie auch verwenden Stream.generate.

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

und Sie können es bei Bedarf in eine einfache Dienstprogrammmethode einbinden:

public static String repeat(String str, int times) {
   return Stream.generate(() -> str).limit(times).collect(joining());
}
Alexis C.
quelle
6
... oder return IntStream.range(0, times).mapToObj(i -> str).collect(joining());was besser parallelisiert
Alexis C.
32

Sie möchten also Schleifen vermeiden?

Hier hast du es:

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

(Natürlich weiß ich, dass dies hässlich und ineffizient ist, aber es hat keine Schleifen :-p)

Sie wollen es einfacher und schöner? benutze jython:

s * 3

Edit : lass es uns ein bisschen optimieren :-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 : Ich habe einen schnellen und schmutzigen Benchmark für die 4 Hauptalternativen durchgeführt, aber ich habe keine Zeit, ihn mehrmals auszuführen, um die Mittelwerte zu ermitteln und die Zeiten für mehrere Eingaben zu zeichnen ... Also hier ist der Code, wenn jemand möchte es versuchen:

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

Es werden 2 Argumente benötigt, das erste ist die Anzahl der Iterationen (jede Funktion wird mit den Wiederholungszeiten arg von 1..n ausgeführt) und das zweite ist die zu wiederholende Zeichenfolge.

Bisher lässt eine schnelle Überprüfung der Zeiten mit unterschiedlichen Eingaben das Ranking in etwa so (besser zu schlechter):

  1. Iterativer StringBuilder-Anhang (1x).
  2. Rekursive Verkettung log2-Aufrufe (~ 3x).
  3. Rekursive Verkettung linearer Aufrufe (~ 30x).
  4. Iterative Verkettung linear (~ 45x).

Ich hätte nie gedacht, dass die rekursive Funktion schneller ist als die forSchleife: -o

Viel Spaß (ctional xD).

fortran
quelle
1
+1 für Rekursion und offensichtlich ein Lisp-Hacker. Ich denke auch nicht, dass dies so ineffizient ist. Die Verkettung von Strings ist nicht das Kriegsverbrechen, das es einmal war, weil + wirklich nur ein StringBuilder-UTH ist. Siehe stackoverflow.com/questions/47605/java-string-concatenation und schneide.wordpress.com/2009/02/23/… . Ich frage mich, wie viel all diese Stack-Pushs und Pops von den Rekursionskosten abhängen oder ob sich der Hotspot darum kümmert. Ich wünschte wirklich, ich hätte die freie Zeit, um es zu bewerten. Jemand anderes vielleicht?
Ethan Heilman
@ e5: fortran ist richtig; Diese Lösung könnte effizienter gestaltet werden. Diese Implementierung erstellt unnötigerweise einen neuen StringBuilder (und einen neuen String) für jede Rekursion. Trotzdem eine schöne Lösung.
Rob
3
@ e5 Ich wünschte, ich wäre ein Lisp-Hacker xD ... Wenn ich es wäre, hätte ich eine rekursive Schwanzfunktion verwendet :-p
fortran
1
Microbenchmarks funktionieren in Java nicht gut. Der Versuch, die Geschwindigkeit Ihrer Implementierungen so zu messen, ist nicht gut.
ceklock
@tecnotron Ich weiß, aber sie sind immer noch besser als nichts ... Und die einzige "Überraschung" war der geringfügige Unterschied zwischen der Verkettung der naiven Schleife und der linearen Rekursion.
Fortan
20

Dies enthält weniger Zeichen als Ihre Frage

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();
}
Pyrolistisch
quelle
4
Es enthält mehr Zeichen als meine Antwort StringUtils.repeat (str, n).
Ethan Heilman
8
Sofern Sie Apache Commons nicht bereits verwenden, ist diese Antwort viel einfacher - kein Herunterladen einer anderen Bibliothek, einschließlich dieser in Ihren Klassenpfad, um sicherzustellen, dass ihre Lizenz mit Ihrer kompatibel ist usw.
Paul Tomblin
7
Bitte geben Sie niemals null zurück. In diesem Fall geben Sie eine leere Zeichenfolge zurück, sodass Sie den zurückgegebenen Wert immer ungeprüft verwenden können. Ansonsten würde ich das Poster empfehlen zu verwenden.
Thorbjørn Ravn Andersen
7
Nun, es gibt drei Möglichkeiten, um damit umzugehen, wenn s null ist. 1. Übergeben Sie den Fehler (return null), 2. Verbergen Sie den Fehler (return ""), 3. Werfen Sie eine NPE. Das Ausblenden des Fehlers und das Werfen eines NPE sind nicht cool, daher habe ich den Fehler bestanden.
Pyrolistical
1
@EthanHeilman addiere den Wert von 2MB commons-lang3.3.1-sourcesund du bist nicht mehr so ​​gut;) Aber wenn jemand schon hat commons-lang, unterstütze ich deine Antwort.
TWiStErRob
9

Basierend auf der Antwort von fortran ist dies eine wiederkehrende Version, die einen StringBuilder verwendet:

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
quelle
1
Eine Schleife anstelle einer Rekursion würde die Anzahl der Stapelrahmen für eine große Anzahl von Wiederholungen verringern.
La-comadreja
7

Die Verwendung von Dollar ist so einfach wie die Eingabe:

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

PS: Wiederholen funktioniert auch für Array, Liste, Set usw.

dfa
quelle
3
Wird die assertThat () -Methode wirklich benötigt?
ceklock
7

Ich wollte eine Funktion zum Erstellen einer durch Kommas getrennten Liste von Fragezeichen für JDBC-Zwecke und fand diesen Beitrag. Also entschied ich mich für zwei Varianten und sah, welche besser abschnitt. Nach 1 Million Iterationen dauerte die Gartenart StringBuilder 2 Sekunden (fun1) und die kryptisch vermeintlich optimalere Version (fun2) 30 Sekunden. Was bringt es, wieder kryptisch zu sein?

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
quelle
3
Ich mache Sinn, dass der zweite viel länger dauern würde. Es führt eine Zeichenfolgensuche durch und ändert die Zeichenfolge dann zeichenweise.
Ethan Heilman
7

OOP-Lösung

Fast jede Antwort schlägt eine statische Funktion als Lösung vor, aber wenn ich objektorientiert denke (aus Gründen der Wiederverwendbarkeit und Klarheit), habe ich eine Lösung über die Delegierung über die CharSequence-Schnittstelle gefunden (die auch die Verwendbarkeit für veränderbare CharSequence-Klassen eröffnet).

Die folgende Klasse kann entweder mit oder ohne Separator-String / CharSequence verwendet werden, und jeder Aufruf von "toString ()" erstellt den letzten wiederholten String. Der Input / Separator ist nicht nur auf die String-Klasse beschränkt, sondern kann jede Klasse sein, die CharSequence implementiert (z. B. StringBuilder, StringBuffer usw.)!

Quellcode:

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

Anwendungsbeispiel:

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

Beispielausgabe:

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
quelle
4
selten habe ich ekelhafte Dinge gesehen: /
Ven
6

Wenn Sie nur JRE-Klassen ( System.arraycopy ) verwenden und versuchen, die Anzahl der temporären Objekte zu minimieren, können Sie Folgendes schreiben:

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

BEARBEITEN

und ohne Schleifen können Sie versuchen mit:

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

BEARBEITEN 2

Die Verwendung von Sammlungen ist noch kürzer:

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

Ich mag jedoch immer noch die erste Version.

dfa
quelle
6
-1: um die Hälfte zu schlau. Wenn Sie Code lesbar oder effizient machen möchten, sind diese "Lösungen" keine gute Idee. 'repeat' kann einfach mit einem StringBuilder umgeschrieben werden (Festlegen der Anfangskapazität). Und 'repeat2' / 'repeat3' sind wirklich ineffizient und hängen von der nicht spezifizierten Syntax des von String []. ToString () erzeugten Strings ab.
Stephen C
@Thorb: absolut, mit diesem Code können Sie nicht "Metazeichen", [],
dfa
@Stephen: Die Frage wurde bearbeitet, um explizit keine Schleifen anzufordern . Eine StringBuilder-basierte Antwort wurde bereits bereitgestellt, sodass ich es vermieden habe, ein Duplikat zu veröffentlichen
dfa
@Stephan: Ich kann die Abwertung nicht herausfinden. Meine bearbeitete Antwort ist wie erforderlich schleifenfrei. Es gibt keine Anfragen zur Effizienz. Ich denke, dass diese Frage nur eine intellektuelle Anstrengung ist , um eine Verkettung ohne Schleife zu erzeugen.
dfa
@Stephan: Über Collection.toString (und Arrays.toString) erstellte Zeichenfolgen sind in AbstractCollection.toString eindeutig angegeben : "Die Zeichenfolgendarstellung besteht aus einer Liste der Elemente der Sammlung in der Reihenfolge, in der sie von ihrem Iterator zurückgegeben werden, in eckigen Klammern ( "[]"). Benachbarte Elemente werden durch die Zeichen "," (Komma und Leerzeichen) getrennt. "
dfa
6

Nicht der kürzeste, aber (glaube ich) der schnellste Weg ist die Verwendung des 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
quelle
5

Wenn Geschwindigkeit Ihr Anliegen ist, sollten Sie so wenig Speicher wie möglich kopieren. Daher ist es erforderlich, mit Arrays von Zeichen zu arbeiten.

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

Um die Geschwindigkeit zu testen, sieht eine ähnliche optimale Methode mit StirngBuilder folgendermaßen aus:

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

und den Code zum Testen:

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

}

Und hier resultiert der Lauf aus meinem System:

elapsed repeatString: 6006571
elapsed repeatStringSB: 9064937

Beachten Sie, dass der Test für die Schleife darin besteht, JIT einzuschalten und optimale Ergebnisse zu erzielen.

Panayotis
quelle
4

aus Gründen der Lesbarkeit und Portabilität:

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

Wenn Sie sich Sorgen um die Leistung machen, verwenden Sie einfach einen StringBuilder in der Schleife und führen Sie beim Beenden der Schleife einen .toString () aus. Schreiben Sie Ihre eigene Util-Klasse und verwenden Sie sie erneut. 5 Codezeilen max.

WolfmanDragon
quelle
2

Ich genieße diese Frage wirklich. Es gibt viel Wissen und Stile. Also kann ich es nicht verlassen, ohne meinen Rock and Roll zu zeigen;)

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

Gefällt es dir

Daniel De León
quelle
1
In meinem extremeren Test produziere ich eine Saitenwiederholungslänge von 1.700.000.000 (1,7 Gigas) mit -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);
}
Falke
quelle
2

Einfache Schleife

public static String repeat(String string, int times) {
    StringBuilder out = new StringBuilder();
    while (times-- > 0) {
        out.append(string);
    }
    return out.toString();
}
Codler
quelle
2
Übergabe timesan den StringBuilder-Konstruktor.
Behrouz.M
2

Probieren Sie es aus:

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

quelle
2

Mit der Rekursion können Sie Folgendes tun (mit ternären Operatoren, maximal eine Zeile):

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

Ich weiß, es ist hässlich und wahrscheinlich nicht effizient, aber es ist eine Zeile!

HyperNeutrino
quelle
Dies ist der Ansatz, den ich verfolgen würde, aber warum mehr Überprüfungen durchführen als erforderlich? Rückgabennummer> 0? Zeichenfolge + Wiederholung (Zeichenfolge, Nummer 1): "";
Fering
Oh, scheint niczm25 unten damit geantwortet zu haben
Fering
@ Hauptgrund dafür, dass dieser Weg immer O (log N) Durchschnitt und nicht O (N) ist. Etwas mehr Optimierung als die andere, aber trotzdem schlecht.
HyperNeutrino
2

Eine einfache einzeilige Lösung:
erfordert Java 8

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

Trotz Ihres Wunsches, keine Schleifen zu verwenden, sollten Sie eine Schleife verwenden.

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

Ihre Gründe, eine for-Schleife nicht zu verwenden, sind nicht gut. Als Antwort auf Ihre Kritik:

  1. Welche Lösung Sie auch verwenden, sie wird mit ziemlicher Sicherheit länger dauern. Wenn Sie eine vorgefertigte Funktion verwenden, wird sie nur unter mehr Abdeckungen verstaut.
  2. Jemand, der Ihren Code liest, muss herausfinden, was Sie in dieser Nicht-for-Schleife tun. Angesichts der Tatsache, dass eine for-Schleife der idiomatische Weg ist, dies zu tun, wäre es viel einfacher herauszufinden, ob Sie dies mit einer for-Schleife getan haben.
  3. Ja jemand könnte etwas Gescheites hinzufügen, aber durch eine for - Schleife zu vermeiden Sie sind etwas zu tun klug . Das ist so, als würde man sich absichtlich in den Fuß schießen, um nicht versehentlich in den Fuß zu schießen.
  4. Off-by-One-Fehler sind mit einem einzigen Test unglaublich leicht zu erkennen. Da Sie Ihren Code testen sollten, sollte ein Fehler nach dem anderen leicht zu beheben und zu erkennen sein. Und es ist erwähnenswert: Der obige Code enthält keinen Fehler nach dem anderen. For-Loops sind ebenso einfach richtig zu machen.
  5. Verwenden Sie also keine Variablen wieder. Das ist nicht die Schuld der for-Schleife.
  6. Dies gilt auch für die von Ihnen verwendete Lösung. Und wie ich schon sagte; Ein Bug-Jäger erwartet wahrscheinlich, dass Sie dies mit einer for-Schleife tun, damit er sie leichter findet, wenn Sie eine for-Schleife verwenden.
Imagist
quelle
3
-1. Hier sind zwei Übungen für Sie: a) Führen Sie Ihren Code mit aus repetitions = -5. b) Commons Lang herunterladen und repeatString('a', 1000)millionenfach in einer Schleife ausführen ; Machen Sie dasselbe mit Ihrem Code. Vergleiche die Zeiten. Für zusätzliche Gutschrift machen Sie dasselbe mit repeatString('ab', 1000).
ChssPly76
2
Argumentieren Sie, dass Ihr Code dann besser lesbar ist StringUtils.repeat("ab",1000)? Denn das war meine Antwort, die Sie abgelehnt haben. Es funktioniert auch besser und hat keine Fehler.
ChssPly76
2
Lesen Sie den 2. Satz in der Frage, die Sie zitieren. "Ich versuche, Schleifen um jeden Preis zu vermeiden, weil" der Frage als Klarstellung als Antwort auf Andrew Hares Antwort nach meiner Antwort hinzugefügt wurde - nicht, dass es wichtig ist, denn wenn die Position, die Sie einnehmen, lautet "Antwort ist schlecht, wenn Schleife ist benutzt überall "gibt es keine Antworten auf die OP Frage. Sogar die Lösungen von dfa - so erfinderisch sie auch sind - werden für Schleifen im Inneren verwendet. "jar hell" wurde oben geantwortet; commons lang wird ohnehin in jeder Anwendung mit anständiger Größe verwendet und fügt daher keine neue Abhängigkeit hinzu.
ChssPly76
2
@ ChssPly76 an diesem Punkt bin ich mir ziemlich sicher, dass der Imagist trollt. Es fällt mir wirklich schwer zu sehen, wie jemand lesen kann, was ich geschrieben habe, und ernsthaft über die oben eingegebenen Antworten nachzudenken.
Ethan Heilman
1
@ ChssPly76 meine Antworten haben überhaupt keine Schleifen :-p
fortran
1

Wenn Sie nur die Länge der Ausgabezeichenfolge kennen (und diese möglicherweise nicht durch die Länge der Eingabezeichenfolge teilbar ist), verwenden Sie diese Methode:

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

Nutzungsdemo:

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

Nicht mit leer verwenden s und length> 0 verwenden, da in diesem Fall nicht das gewünschte Ergebnis erzielt werden kann.

John McClane
quelle
0

Hier ist die neueste Version von 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();
    }
    }

Es muss nicht einmal so groß sein, kann daraus gemacht werden und kann kopiert und in eine Utility-Klasse in Ihrem Projekt eingefügt werden.

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

Also e5, ich denke, der beste Weg, dies zu tun, wäre einfach den oben genannten Code oder eine der Antworten hier zu verwenden. Aber Commons Lang ist einfach zu groß, wenn es sich um ein kleines Projekt handelt

alexmherrmann
quelle
Ich glaube nicht, dass Sie noch viel mehr tun können ... vielleicht sogar eine Menge!
Alexmherrmann
0

Ich habe eine rekursive Methode erstellt, die genau das tut, was Sie wollen. Sie können diese verwenden ...

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

Ich habe die gleiche Antwort auf Kann ich Strings in Java multiplizieren, um Sequenzen zu wiederholen?

niczm25
quelle
Unnötige Neuzuweisung von Zeichenfolgen und Rekursionsaufwand ... schlecht, schlecht, nicht gut.
Alexander - Stellen Sie Monica
1
Dies wird langsam sein. Nicht empfohlen! Verwenden Sie StringBuilderstattdessen.
Svetlin Nakov
0
public static String rep(int a,String k)

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

Sie können diese rekursive Methode für Ihr gewünschtes Ziel verwenden.

Syed Salman Hassan
quelle