800 Stimmen

Einfacher Weg zur Wiederholung einer Zeichenkette

Ich suche eine einfache Commons-Methode oder einen Operator, der es mir erlaubt, eine Zeichenkette zu wiederholen n Zeiten. Ich weiß, ich könnte dies mit einer for-Schleife schreiben, aber ich möchte for-Schleifen vermeiden, wann immer es nötig ist, und eine einfache direkte Methode sollte irgendwo existieren.

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

repeated.equals("abcabcabc");

Verwandt mit:

wiederholen string javascript Erstellen von NSString durch Wiederholung einer anderen Zeichenfolge eine bestimmte Anzahl von Malen

Bearbeitet

Ich versuche, for-Schleifen zu vermeiden, wenn sie nicht unbedingt notwendig sind, denn:

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

  2. Jemand, der meinen Code liest, muss herausfinden, was ich in dieser for-Schleife tue. Selbst wenn der Code kommentiert ist und aussagekräftige Variablennamen hat, muss er sich vergewissern, dass er nicht irgendetwas "Schlaues" macht.

  3. Programmierer lieben es, clevere Dinge in for-Schleifen einzubauen. Selbst wenn ich schreibe, dass die Schleife "nur das tut, was sie tun soll", schließt das nicht aus, dass jemand vorbeikommt und eine zusätzliche clevere "Lösung" einbaut.

  4. Sie sind oft sehr leicht zu verwechseln. For-Schleifen, die Indizes beinhalten, neigen dazu, Fehler zu erzeugen, die sich um einen Fehler unterscheiden.

  5. In For-Schleifen werden oft dieselben Variablen wiederverwendet, was die Wahrscheinlichkeit von schwer zu findenden Scoping-Fehlern erhöht.

  6. Denn Schleifen erhöhen die Anzahl der Stellen, an denen ein Wanzenjäger suchen muss.

48 Stimmen

Ich weiß, dass for-Schleifen einige echte Probleme verursachen können. Aber man sollte nicht versuchen, for-Schleifen "um jeden Preis" zu vermeiden, denn wenn das auf Kosten von Lesbarkeit, Wartbarkeit und Geschwindigkeit geht, ist das kontraproduktiv. Dies ist einer dieser Fälle.

2 Stimmen

Daher sollten Sie sich überlegen, ob Sie für Ihren Code nicht ausführliche Unittests durchführen wollen. So können Sie die Unit-Tests einfach anzeigen, wenn eine Dokumentation benötigt wird.

10 Stimmen

"Sie erhöhen die Anzahl der Codezeilen, auch wenn sie in einer anderen Funktion versteckt sind"... wow, einfach wow. Big-O, nicht LoC

104voto

I. J. Kennedy Punkte 23203

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

5 Stimmen

Erstaunlich :-) Aber Vorsicht, n wird zu Null !

0 Stimmen

java.sun.com/j2se/1.4.2/docs/api/java/lang/ - replace() akzeptiert zwei char-Parameter. Nicht Strings!

1 Stimmen

Ich glaube, er meinte replaceAll

88voto

Jack Punkte 19711

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

Strings.repeat("-", 60);

4 Stimmen

... und erhalten 3Mb neue Abhängigkeiten.

6 Stimmen

@MonoThreaded Ich dachte, es wäre selbstverständlich, aber Sie sollten Guava nicht einbinden, nur um einen String zu wiederholen. Meine Antwort war über, wenn Sie bereits mit Guava sowieso dann ist dies, wie Sie es tun würde.

55voto

Alexis C. Punkte 87062

Mit java-8 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 sie bei Bedarf in eine einfache Dienstprogrammmethode einpacken:

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

7 Stimmen

... oder return IntStream.range(0, times).mapToObj(i -> str).collect(joining()); die besser parallelisiert

0 Stimmen

Stream.of(new String[times]).map(n -> "abc").collect(Collectors.joining());

33voto

fortran Punkte 70744

Sie wollen also Schleifen vermeiden?

Hier haben Sie 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)

Wenn Sie es einfacher und schöner haben wollen, verwenden Sie Jython:

s * 3

Editar : Lass uns das 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);
}

Bearbeiten2 : Ich habe einen schnellen und schmutzigen Benchmark für die 4 Hauptalternativen gemacht, aber ich habe nicht die Zeit, ihn mehrmals laufen zu lassen, um die Mittelwerte zu erhalten und die Zeiten für mehrere Eingaben aufzuzeichnen... Hier ist also der Code, falls jemand ihn ausprobieren möchte:

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

Sie benötigt 2 Argumente, das erste ist die Anzahl der Iterationen (jede Funktion läuft mit Wiederholungszeiten arg von 1..n) und das zweite ist die zu wiederholende Zeichenkette.

Wenn man sich die Zeiten ansieht, die mit verschiedenen Eingaben gelaufen sind, sieht die Rangliste in etwa so aus (besser bis schlechter):

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

Ich wäre nie auf die Idee gekommen, dass die rekursive Funktion schneller ist als die for Schleife :-o

Viel Spaß(ctional xD).

1 Stimmen

+1 für Rekursion und weil ich offensichtlich ein Lisp-Hacker bin. Ich glaube auch nicht, dass dies so ineffizient ist, String-Verkettung ist nicht das Kriegsverbrechen, das es einmal war, weil + wirklich nur ein stringBuilder UTH ist. Siehe stackoverflow.com/questions/47605/java-string-concatenation y schneide.wordpress.com/2009/02/23/ . Ich frage mich, wie viel all diese Stack-Pushes und -Pops aus der Rekursion kosten, oder ob Hotspot kümmert sich um sie. Ich wünschte wirklich, ich hätte die freie Zeit, um einen Benchmark durchzuführen. Jemand anderes vielleicht?

0 Stimmen

@e5: fortran hat Recht; diese Lösung könnte noch effizienter gestaltet werden. Diese Implementierung wird unnötigerweise einen neuen StringBuilder (und einen neuen String) für jede Rekursion erstellen. Trotzdem eine schöne Lösung.

3 Stimmen

@e5 Ich würde mir wünschen, ich wäre ein Lisp-Hacker xD... Wenn ich es wäre, hätte ich eine rekursive Schwanzfunktion verwendet :-p

21voto

Pyrolistical Punkte 26854

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

7 Stimmen

Sie enthält mehr Zeichen als meine Antwort StringUtils.repeat(str, n).

9 Stimmen

Sofern Sie nicht bereits Apache Commons verwenden, ist diese Antwort viel weniger mühsam - Sie müssen keine weitere Bibliothek herunterladen, sie in Ihren Klassenpfad aufnehmen, sicherstellen, dass ihre Lizenz mit Ihrer kompatibel ist usw.

7 Stimmen

Bitte geben Sie niemals null zurück - in diesem Fall geben Sie einen leeren String zurück, so dass Sie den zurückgegebenen Wert immer ungeprüft verwenden können. Andernfalls würde ich dem Poster empfehlen, dies zu verwenden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X