Modo semplice per ripetere una stringa in Java


598

Sto cercando un metodo o un operatore comune comune che mi permetta di ripetere alcune stringhe n volte. So che potrei scrivere questo usando un ciclo for, ma desidero evitare i loop ogni volta che è necessario e un metodo diretto semplice dovrebbe esistere da qualche parte.

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

repeated.equals("abcabcabc");

Correlato a:

repeat string javascript Crea NSString ripetendo un'altra stringa un determinato numero di volte

Modificato

Cerco di evitare i loop quando non sono completamente necessari perché:

  1. Si aggiungono al numero di righe di codice anche se sono nascoste in un'altra funzione.

  2. Qualcuno che legge il mio codice deve capire cosa sto facendo in quel ciclo. Anche se è commentato e ha nomi di variabili significativi, devono comunque assicurarsi che non stia facendo nulla di "intelligente".

  3. I programmatori amano mettere cose intelligenti per i loop, anche se lo scrivo per "fare solo quello che è destinato a fare", ciò non impedisce a qualcuno di venire e aggiungere qualche "correzione" aggiuntiva.

  4. Molto spesso sono facili da sbagliare. Per i loop che coinvolgono gli indici tendono a generarsi da un bug.

  5. Perché i loop riutilizzano spesso le stesse variabili, aumentando la possibilità di trovare bug di scoping davvero difficili.

  6. Perché i loop aumentino il numero di posti in cui deve cercare un cacciatore di bug.


35
Capisco che per i loop può causare alcuni problemi reali. Ma non dovresti cercare di evitare i loop "a tutti i costi" perché se ti costa leggibilità, manutenibilità e velocità, sei controproducente. Questo è uno di quei casi.
Imagist

9
"Si aggiungono al numero di righe di codice anche se sono nascoste in un'altra funzione" ... wow, wow. Big-O, non LoC
Pyrolistical

7
@imagist Sto evitando i loop in situazioni in cui mi costa leggibilità e manutenibilità. Considero la velocità come il problema meno importante qui (un non-problema in effetti). Penso che i loop siano abusati e sto cercando di imparare a utilizzare i loop solo quando sono necessari e non come soluzione predefinita.
Ethan Heilman,

3
@Polimistica Non sto rivendicando prestazioni o benefici asintotici. Piuttosto, dicendo che scrivendo meno codice e utilizzando le funzioni di libreria anziché reinventare la ruota, riduco l'area di bug (Lines of Code) aumentando la leggibilità. Entrambe le cose buone sono sicuro che sarai d'accordo.
Ethan Heilman,

4
@ e5; scusami per la pubblicazione dopo anni. Trovo questa domanda così appropriata. Se inseriti in un metodo, gli argomenti dovrebbero essere testati (tempi> = 0), errori generati ecc. Ciò aggiunge robustezza ma anche righe di codice da leggere. Ripetere una stringa è qualcosa di inequivocabile. Chi legge il codice sa esattamente cosa fa una stringa. Ripeti, anche senza una riga di commento o javadoc. Se utilizziamo una libreria stabile, è ragionevole pensare che una funzione così semplice non abbia bug, ANCORA introduce una qualche forma di controllo della "robustezza" di cui dobbiamo anche preoccuparci. Se potessi chiedere 10 miglioramenti, questo (tipo di) cose sarebbe uno.
AgostinoX,

Risposte:


230

String::repeat

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

Novità di Java 11 è il metodo String::repeatche fa esattamente quello che hai chiesto:

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

Il suo Javadoc dice:

/**
 * Returns a string whose value is the concatenation of this
 * string repeated {@code count} times.
 * <p>
 * If this string is empty or count is zero then the empty
 * string is returned.
 *
 * @param count number of times to repeat
 *
 * @return A string composed of this string repeated
 * {@code count} times or the empty string if this
 * string is empty or count is zero
 *
 * @throws IllegalArgumentException if the {@code count} is
 * negative.
 *
 * @since 11
 */ 

2
@Nicolai codice sorgente, nel caso in cui qualcuno si preoccupi hg.openjdk.java.net/jdk/jdk/file/fc16b5f193c7/src/java.base/…
Eugene,

7
non ho ancora visto java 9 per strada (e non lo farò per molto tempo ..) - e apparentemente l' 11 è pronto per la spedizione ..
javadba

1
Probabilmente ovvio, ma puoi chiamare questo metodo anche su una stringa letterale:"abc".repeat(3)
Boann

895

Ecco la versione più breve (richiesto Java 1.5+):

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

Dov'è nil numero di volte che vuoi ripetere la stringa es è la stringa da ripetere.

Non sono necessarie importazioni o librerie.


22
Non penso che sia offuscato affatto. I tipi primitivi ( char[], in questo caso) vengono istanziati con valori null, quindi Stringviene creato un da char[], e i valori null sono replaced()con il carattere desideratos
Amarok,

32
Anche se questo è molto intelligente (+1) penso che dimostri praticamente che il punto dei loop spesso porta a un codice più chiaro
Richard Tingle,

75
Per coloro che si lamentano dell'offuscamento, la leggibilità dipende dall'alfabetizzazione. Questo è perfettamente chiaro in ciò che fa ed educativo per coloro che potrebbero non vederlo subito. Questo è ciò che ottieni con Java a proposito.
dansalmo,

11
Per prestazioni migliori ...replace('\0', str)dovrebbe essere usato al posto della versione String.
user686249

11
@ user686249: ci sono solo replace(char oldChar, char newChar)e replace(CharSequence target, CharSequence replacement)quindi non vedo come potrebbe funzionare
user102008

334

Se stai usando Java <= 7 , questo è "conciso" in quanto ottiene:

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

In Java 8 e versioni successive esiste un modo più leggibile:

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

Infine, per Java 11 e versioni successive, esiste un nuovo repeat​(int count)metodo specifico per questo ( collegamento )

"abc".repeat(12);

In alternativa, se il tuo progetto utilizza librerie Java ci sono più opzioni.

Per Apache Commons :

StringUtils.repeat("abc", 12);

Per Google Guava :

Strings.repeat("abc", 12);

4
Il primo provoca un'eccezione quando nè zero.
saka1029,

L'esempio java 8 non viene compilato -> Tipo mismatch: impossibile convertire da List <Character> a CharSequence
Arigion

6
@Arigion sdeve essere String, non unChar
Caner

@Caner Grazie. Mio male, mi scuso. Apparentemente ero troppo stanco ieri. Mi dispiace per il downvoting. Rimuoverò il downvote non appena potrò (è bloccato fino alla modifica della domanda)
Arigion

@Arigion nessun problema, chiarito ora che s è una stringa
Caner

312

Commons Lang StringUtils.repeat ()

Uso:

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

repeated.equals("abcabcabc");

99
usare una dipendenza con un metodo per il bene della semplicità a lungo termine può provocare un inferno-barattolo
dfa

81
Certo, tranne che per i beni comuni. Non credo di aver mai visto un progetto di oltre 5000 LOCS che non avesse una lingua comune.
Ethan Heilman,

11
Commons Lang è open source: scaricalo e dai un'occhiata. Ovviamente ha un ciclo all'interno, ma non è così semplice. Sono stati fatti molti sforzi per profilare e ottimizzare tale implementazione.
ChssPly76,

28
Non evito i loop per motivi di prestazioni (leggi i miei motivi nella domanda). Quando qualcuno vede StringUtils.repeat, sa cosa sto facendo. Non devono preoccuparsi che ho tentato di scrivere la mia versione di ripetizione e ho fatto un errore. È un'unità cognitiva atomica!
Ethan Heilman,

7
@ Thorbjørn Ravn Andersen - può diventare MOLTO più interessante se le cose continuano a essere estratte dal contesto
ChssPly76

140

Java 8 String.joinoffre un modo ordinato per farlo in combinazione con Collections.nCopies:

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

7
grazie! Per Android TextUtils.join () può essere utilizzato al posto di String.join ()
MinaHany l'

2
Grazie per questa risposta Sembra essere il modo più pulito senza utilizzare alcun metodo di utilità API esterna o! molto bene!!
Andreas M. Oberheim,

2
La cosa bella di questo metodo è che con join puoi fornire un carattere separatore che risulta molto utile se, ad esempio, stai costruendo un elenco CSV. Con tutti gli altri metodi hai un carattere di unione che deve essere rimosso in un'operazione separata.
DroidOS,

102

Ecco un modo per farlo usando solo le funzioni String standard e nessun loop esplicito:

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

5
Incredibile :-) Anche se attenzione a non diventare zero ...!
Yang Meyer,

4
@Vijay Dev & fortran: No, intendeva dire replace(). In Java 1.5+, esiste una versione sovraccarica replace()che richiede due CharSequences (che includono Strings): download.oracle.com/javase/1.5.0/docs/api/java/lang/…
user102008

79
Ahia. Questo è brutto.
Karel Bílek,

8
@mzuba diciamo n=3: prima formatta una stringa per assomigliare a qualcosa %03d( %%è per sfuggire al segno di percentuale), che è il codice di formattazione per aggiungere 3 zero di riempimento, quindi formatta 0con quello, portando a 000, e infine sostituisce ciascuno 0con le stringhe
fortran,

15
Puoi rendere la soluzione meno brutta e più facile da capire: String.format ("% 0" + n + "d", 0) .replace ("0", s)
Artur,

87

Se sei come me e vuoi utilizzare Google Guava e non Apache Commons. Puoi usare il metodo repeat nella classe Guava Strings.

Strings.repeat("-", 60);

2
... e ottieni 3 Mb di nuove dipendenze.
MonoThreaded

6
@MonoThreaded Ho pensato che sarebbe stato ovvio, ma non includere la guava solo per ripetere una stringa. La mia risposta era se stai già usando la guava, allora è così che lo faresti.
Jack,

53

Con , puoi anche usare Stream.generate.

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

e puoi avvolgerlo in un semplice metodo di utilità, se necessario:

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

6
... o return IntStream.range(0, times).mapToObj(i -> str).collect(joining());che parallelizza meglio
Alexis C.

32

Quindi vuoi evitare i loop?

Ecco qui:

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

(ovviamente so che questo è brutto e inefficiente, ma non ha anelli :-p)

Lo vuoi più semplice e più bello? usa jython:

s * 3

modificare : ottimizziamolo un pochino MrGreen

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 : ho fatto un benchmark rapido e sporco per le 4 alternative principali, ma non ho tempo di eseguirlo più volte per ottenere i mezzi e tracciare i tempi per diversi input ... Quindi ecco il codice se qualcuno vuole per provarlo:

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

Sono necessari 2 argomenti, il primo è il numero di iterazioni (ogni funzione viene eseguita con ripetizione arg da 1..n) e la seconda è la stringa da ripetere.

Finora, una rapida ispezione dei tempi di esecuzione con input diversi lascia la classifica qualcosa del genere (meglio o peggio):

  1. Appendice StringBuilder Iterative (1x).
  2. Invocazioni del log2 concatenazione ricorsiva (~ 3x).
  3. Invocazioni lineari di concatenazione ricorsiva (~ 30x).
  4. Concatenazione iterativa lineare (~ 45x).

Non avrei mai immaginato che la funzione ricorsiva fosse più veloce di for ciclo Eeeek !!!

Buon divertimento (ctional xD).


1
+1 per la ricorsione e ovviamente per essere un hacker lisp. Non credo sia neanche così inefficiente, la concatenazione di stringhe non è il crimine di guerra di una volta, perché + in realtà è solo un stringBuilder UTH. Vedi stackoverflow.com/questions/47605/java-string-concatenation e schneide.wordpress.com/2009/02/23/… . Mi chiedo quanto tutti questi stack spingano e saltano dal costo di ricorsione, o se l'hotspot si prende cura di loro. Vorrei davvero avere il tempo libero per confrontarlo. Qualcun altro forse?
Ethan Heilman,

@ e5: fortran ha ragione; questa soluzione potrebbe essere resa più efficiente. Questa implementazione creerà inutilmente un nuovo StringBuilder (e una nuova String) per ogni ricorsione. Comunque una bella soluzione però.
rapina il

3
@ e5 Vorrei essere un hacker di Lisp xD ... Se lo fossi, avrei usato una funzione ricorsiva della coda :-p
fortran,

1
I microbenchmark non funzionano bene in Java. Cercare di misurare la velocità delle tue implementazioni in quel modo non va bene.
Ceklock

@tecnotron Lo so, ma sono ancora meglio di niente ... E l'unica "sorpresa" è stata la leggera differenza tra la concatenazione del ciclo ingenuo e la ricorsione lineare.
fortran,

20

Questo contiene meno caratteri della tua domanda

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

4
Contiene più caratteri della mia risposta StringUtils.repeat (str, n).
Ethan Heilman,

8
A meno che tu non stia già utilizzando Apache Commons, questa risposta è molto meno problematica: non scaricare un'altra libreria, inclusa quella nel tuo percorso di classe, assicurandoti che la sua licenza sia compatibile con la tua, ecc.
Paul Tomblin,

7
Per favore, non restituire mai null - in tal caso restituire una stringa vuota, che consente di utilizzare sempre il valore restituito non selezionato. Altrimenti, cosa consiglierei di usare il poster.
Thorbjørn Ravn Andersen,

7
Bene, ci sono tre modi per gestire se s è null. 1. Passare l'errore (return null), 2. Nascondere l'errore (return ""), 3. Gettare un NPE. Nascondere l'errore e lanciare un NPE non sono interessanti, quindi ho superato l'errore.
Pirolistico

1
@EthanHeilman aggiungi il valore di 2 MB commons-lang3.3.1-sourcese non sei più così bravo;) Ma se qualcuno lo ha già commons-lang, sostengo la tua risposta.
TWiStErRob,

9

sulla base della risposta di Fortran, questa è una versione inaffidabile che utilizza 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();
}

1
il looping piuttosto che la ricorsione ridurrebbe il numero di frame dello stack per un gran numero di ripetizioni.
La-comadreja,

7

usare Dollar è semplice come scrivere:

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

PS: repeat funziona anche per array, List, Set, ecc


3
è davvero necessario il metodo assertThat ()?
Ceklock,

7

Volevo una funzione per creare un elenco delimitato da virgole di punti interrogativi per scopi JDBC e ho trovato questo post. Quindi, ho deciso di prendere due varianti e vedere quale ha funzionato meglio. Dopo 1 milione di iterazioni, StringBuilder, varietà da giardino, ha impiegato 2 secondi (fun1) e la versione criptica presumibilmente più ottimale (fun2) ha impiegato 30 secondi. Qual è il punto di essere di nuovo criptico?

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

3
Ho senso che il secondo richiederebbe molto più tempo. Sta eseguendo una ricerca di stringhe e quindi modificando la stringa carattere per carattere.
Ethan Heilman,

7

Soluzione OOP

Quasi ogni risposta propone una funzione statica come soluzione, ma pensando all'Oggetto (per scopi di riusabilità e chiarezza) ho trovato una soluzione tramite delega attraverso l'interfaccia CharSequence (che apre anche l'usabilità su classi CharSequence mutabili).

La seguente classe può essere utilizzata con o senza Separator-String / CharSequence e ogni chiamata a "toString ()" crea la stringa ripetuta finale. Input / Separator non sono limitati solo a String-Class, ma possono essere tutte le Class che implementano CharSequence (es. StringBuilder, StringBuffer, ecc.)!

Codice sorgente:

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

Uso-Esempio:

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

Esempio-Output:

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

4
raramente ho visto cose disgustose: /
Ven

6

usando solo le classi JRE ( System.arraycopy ) e cercando di ridurre al minimo il numero di oggetti temporanei puoi scrivere qualcosa del tipo:

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

MODIFICARE

e senza loop puoi provare con:

public static String repeat2(String toRepeat, int times) {
    if (toRepeat == null) {
        toRepeat = "";
    }

    if (times < 0) {
        times = 0;
    }

    String[] copies = new String[times];
    Arrays.fill(copies, toRepeat);
    return Arrays.toString(copies).
              replace("[", "").
              replace("]", "").
              replaceAll(", ", "");
}

MODIFICA 2

l'utilizzo delle Collezioni è ancora più breve:

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

comunque mi piace ancora la prima versione.


6
-1: troppo intelligente della metà. Se il tuo obiettivo è renderti leggibile o efficiente, queste "soluzioni" non sono una buona idea. 'ripeti' potrebbe semplicemente essere riscritto usando StringBuilder (impostazione della capacità iniziale). E 'repeat2' / 'repeat3' sono davvero inefficienti e dipendono dalla sintassi non specificata della stringa prodotta da String []. ToString ().
Stephen C,

@Thorb: assolutamente, con questo codice non puoi usare "metacharacter", [],
dfa,

@Stephen: la domanda è stata modificata per richiedere esplicitamente nessun loop. Era già stata fornita una risposta basata su StringBuilder, quindi ho evitato di pubblicare un duplicato
dfa

@Stephan: non riesco a capire il downvote. La mia risposta modificata è senza loop come richiesto. Non ci sono richieste sull'efficienza. Penso che questa domanda sia solo uno sforzo intellettuale per produrre una concatenazione senza loop.
dfa,

@Stephan: Le stringhe prodotte tramite Collection.toString (e Arrays.toString) sono chiaramente specificate in AbstractCollection.toString: "La rappresentazione delle stringhe consiste in un elenco degli elementi della raccolta nell'ordine in cui sono restituiti dal suo iteratore, racchiuso tra parentesi quadre ( "[]"). Gli elementi adiacenti sono separati dai caratteri "," (virgola e spazio). "
dfa,

6

Non il più breve, ma (penso) il modo più veloce è usare 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

Se la velocità è la tua preoccupazione, allora dovresti usare meno memoria possibile. Pertanto è necessario lavorare con matrici di caratteri.

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

Per testare la velocità, un metodo ottimale simile usando StirngBuilder è il seguente:

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

e il codice per testarlo:

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

}

E qui i risultati della corsa dal mio sistema:

elapsed repeatString: 6006571
elapsed repeatStringSB: 9064937

Si noti che il test per il loop è di avviare JIT e ottenere risultati ottimali.


4

per motivi di leggibilità e portabilità:

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

3

Se sei preoccupato per le prestazioni, usa semplicemente StringBuilder all'interno del loop ed esegui un .toString () all'uscita del Loop. Diamine, scrivi la tua Classe Util e riutilizzala. 5 righe di codice max.


2

Mi piace davvero questa domanda. C'è molta conoscenza e stili. Quindi non posso lasciarlo senza mostrare il mio 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);
    }
}

Ti piace?


1
Nel mio test più estremo, produco una lunghezza di ripetizione di 1.700.000.000 (1,7 gigas), usando -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);
}

2

Loop semplice

public static String repeat(String string, int times) {
    StringBuilder out = new StringBuilder();
    while (times-- > 0) {
        out.append(string);
    }
    return out.toString();
}

2
passare timesal costruttore StringBuilder.
Behrouz.M

2

Prova questo:

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

Utilizzando la ricorsione, è possibile effettuare le seguenti operazioni (utilizzando operatori ternari, max una riga):

public static final String repeat(String string, long number) {
    return number == 1 ? string : (number % 2 == 0 ? repeat(string + string, number / 2) : string + repeat(string + string, (number - 1) / 2));
}

Lo so, è brutto e probabilmente non efficiente, ma è una linea!


Questo è l'approccio che prenderei, ma perché fare più controlli di quanto sia necessario? numero di ritorno> 0? stringa + ripetizione (stringa, numero 1): "";
Fering

Oh, sembra che niczm25 abbia risposto di seguito
Fering

@Specificare il motivo principale in modo che questa sia sempre O (log N) anziché O (N). Ottimizzazione leggermente maggiore rispetto all'altra, sebbene comunque cattiva.
HyperNeutrino,

2

una soluzione a una riga semplice:
richiede Java 8

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

1

Nonostante il tuo desiderio di non usare i loop, penso che dovresti usare un loop.

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

Le tue ragioni per non usare un ciclo for non sono buone. In risposta alle tue critiche:

  1. Qualunque soluzione tu usi sarà quasi sicuramente più lunga di questa. L'uso di una funzione preinstallata lo inserisce solo sotto più cover.
  2. Qualcuno che legge il tuo codice dovrà capire cosa stai facendo in quel non-for-loop. Dato che un for-loop è il modo idiomatico per farlo, sarebbe molto più facile capire se lo hai fatto con un ciclo for.
  3. Sì, qualcuno potrebbe aggiungere qualcosa di intelligente, ma evitando un ciclo for si sta facendo qualcosa di intelligente . È come spararsi intenzionalmente al piede per evitare di spararsi accidentalmente al piede.
  4. Gli errori off-by-one sono anche sorprendentemente facili da cogliere con un singolo test. Dato che dovresti testare il tuo codice, un errore off-to-one dovrebbe essere facile da correggere e rilevare. E vale la pena notare: il codice sopra non contiene un errore off-by-one. Per i loop sono ugualmente facili da ottenere.
  5. Quindi non riutilizzare le variabili. Non è colpa del for-loop.
  6. Ancora una volta, così fa qualunque soluzione tu usi. E come ho notato prima; un cacciatore di bug probabilmente si aspetta che tu faccia questo con un ciclo for, quindi sarà più facile trovarlo se usi un ciclo for.

3
-1. Ecco due esercizi per te: a) eseguire il codice con repetitions = -5. b) scaricare Commons Lang ed eseguire repeatString('a', 1000)un milione di volte in un ciclo; fai lo stesso con il tuo codice; confrontare i tempi. Per credito extra fai lo stesso con repeatString('ab', 1000).
ChssPly76,

2
Stai sostenendo che il tuo codice è più leggibile allora StringUtils.repeat("ab",1000)? Perché quella era la mia risposta che hai annullato il voto. Funziona anche meglio e non ha bug.
ChssPly76,

2
Leggi la seconda frase nella domanda che stai citando. "Cerco di evitare i loop a tutti i costi perché" è stato aggiunto alla domanda come chiarimento in risposta alla risposta di Andrew Hare dopo la mia risposta - non che sia importante perché se la posizione che stai assumendo è "la risposta è negativa se il loop è usato ovunque "non ci sono risposte alla domanda OP. Anche le soluzioni di dfa - per quanto inventive siano - usano per gli anelli all'interno. "jar hell" è stato risposto sopra; commons lang è usato in ogni applicazione di dimensioni decenti e quindi non aggiunge una nuova dipendenza.
ChssPly76,

2
@ ChssPly76 a questo punto sono abbastanza sicuro che l'immaginista stia trollando. Ho davvero difficoltà a vedere come chiunque potesse leggere ciò che ho scritto e pensare seriamente alle risposte sopra digitate.
Ethan Heilman,

1
@ ChssPly76 le mie risposte non hanno alcun loop :-p
fortran

1

Se conosci solo la lunghezza della stringa di output (e potrebbe non essere divisibile per la lunghezza della stringa di input), utilizza questo metodo:

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

Demo d'uso:

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

Non utilizzare con vuoto se length> 0, poiché in questo caso è impossibile ottenere il risultato desiderato.


0

ecco l'ultima 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();
    }
    }

non è nemmeno necessario che sia così grande, può essere trasformato in questo e può essere copiato e incollato in una classe di utilità nel progetto.

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

Quindi e5, penso che il modo migliore per farlo sarebbe semplicemente usare il codice sopra menzionato, o una qualsiasi delle risposte qui. ma la lingua comune è troppo grande se si tratta di un piccolo progetto


Non credo che ci sia molto altro che puoi fare ... scusa forse AOT !!
alexmherrmann,

0

Ho creato un metodo ricorsivo che fa la stessa cosa che vuoi .. sentiti libero di usare questo ...

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

ho la stessa risposta su Posso moltiplicare le stringhe in Java per ripetere le sequenze?


Inutile riallocazione delle stringhe e ricorsione ambientale ... cattiva, cattiva, non buona.
Alexander - Ripristina Monica il

1
Questo sarà lento. Non consigliato! Usa StringBuilderinvece.
Svetlin Nakov,

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

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

È possibile utilizzare questo metodo ricorsivo per l'obiettivo desiderato.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.