Crea una stringa con n caratteri


144

C'è un modo in Java per creare una stringa con un numero specificato di un carattere specificato? Nel mio caso, avrei bisogno di creare una stringa con 10 spazi. Il mio codice attuale è:

StringBuffer outputBuffer = new StringBuffer(length);
for (int i = 0; i < length; i++){
   outputBuffer.append(" ");
}
return outputBuffer.toString();

C'è un modo migliore per realizzare la stessa cosa. In particolare, vorrei qualcosa di veloce (in termini di esecuzione).


1
Se ti ritrovi a farlo molto, basta scrivere una funzione: String characterRepeat (Char c, int Length) {...} che fa quello che fai lì per qualsiasi carattere e lunghezza. Quindi chiamalo quando ne hai bisogno.
Austin Fitzpatrick,

12
vuoi usare StringBuilder invece di StringBuffer

Aggiungi all'inizio la dimensione del buffer, è facile da calcolare e facilita le cose nella gestione della memoria !. StringBuilder outputBuffer = new StringBuilder (repeat * base.length ());
Victor,


Se vuoi aggiungere un singolo spazio, usa append(' ')invece ... comporta un po 'meno elaborazione ...
Erk,

Risposte:


36

Il ciclo for sarà ottimizzato dal compilatore. In casi come il tuo non devi preoccuparti dell'ottimizzazione da solo. Fidati del compilatore.

A proposito, se c'è un modo per creare una stringa con n caratteri spaziali, allora è codificato nello stesso modo come hai appena fatto.


1
anche se questo non è stato ottimizzato - quanto spesso viene creato nel tuo programma - se spesso si salva in una variabile statica o altra cache
mmmmmm

tra Array.fill () scorre semplicemente attraverso l'array. / ho bisogno di più punti per commentare altri post :)
kalkin,

La lunghezza di @Mark è variabile, quindi sembra sciocco salvarla. Cosa dovrei fare, avere un static Dictionary<int, String>?
C. Ross,

1
Conserva la stringa più lunga che desideri, quindi usa qualcosa del genere: " ".substring(0, 10);
mjaggard

169

Probabilmente il codice più breve che utilizza l' StringAPI, esclusivamente:

String space10 = new String(new char[10]).replace('\0', ' ');

System.out.println("[" + space10 + "]");
// prints "[          ]"

Come metodo, senza istanziare direttamente char:

import java.nio.CharBuffer;

/**
 * Creates a string of spaces that is 'spaces' spaces long.
 *
 * @param spaces The number of spaces to add to the string.
 */
public String spaces( int spaces ) {
  return CharBuffer.allocate( spaces ).toString().replace( '\0', ' ' );
}

Richiamare usando:

System.out.printf( "[%s]%n", spaces( 10 ) );

4
Anche se mi piace il one-liner, penso che la soluzione di FrustratedWithFormsDes sia la migliore quando si tratta di esecuzione poiché evita di controllare ciascuna per \ 0 e assegna semplicemente lo spazio.
Bouncner,

1
Testato, questo è sempre più veloce del loop, abbastanza costantemente 50000-100000 nano secondi più veloce, il che potrebbe essere piuttosto significativo (0,1 millisecondi) il loop diventa più veloce all'aumentare del numero di iterazioni sebbene il tempo totale sia ancora elevato. Questo vale per la creazione di stringhe di spazio di dimensioni diverse.
kmecpp

Colpo in su per soluzione abbastanza piacevole, ho usato nella mia risposta qui stackoverflow.com/questions/852665/...
Maytham-ɯɐɥʇʎɐɯ

66

Hmm ora che ci penso, forse Arrays.fill:

char[] charArray = new char[length];
Arrays.fill(charArray, ' ');
String str = new String(charArray);

Ovviamente, suppongo che il fillmetodo faccia la stessa cosa del tuo codice, quindi probabilmente funzionerà allo stesso modo, ma almeno questo è un minor numero di righe.


2
Dopo aver verificato la fonte, sembra che questo effettivamente faccia esattamente ciò che l'OP ha pubblicato: riga 806 di docjar.com/html/api/java/util/Arrays.java.html
Pops

2
@Lord Torgamus La domanda del PO è stata modificata? Perché nella versione che vedo, è in loop su StringBuffer.append () e la versione di Frustrated sta facendo un riempimento (che ovviamente esegue loop assegnando i caratteri all'array). Non è la stessa cosa.
CPerkins,

@CPerkins, abbastanza onesto, non ero chiaro. Il mio punto è che entrambi eseguono un inserimento carattere per carattere all'interno di un ciclo for.
apre

1
@Lord Torgamus - Sono d'accordo. peccato che jvm non possa semplicemente fare un memset. Prezzo che paghiamo per caratteri ampi.
CPerkins,

2
@Pops vale la pena notare che se la Arrays.fill()funzione viene utilizzata in modo coerente per fare questo lavoro attraverso il codice e il codice della libreria (standard ed esterno), allora è un buon candidato per la compilazione di Hotspot e ha maggiori probabilità di trovare la cache della CPU. Le future JVM potrebbero assegnarle come funzione intrinseca . Rolling your ti taglia fuori da tutta questa bontà delle prestazioni ...
SusanW

65

Consiglio vivamente di non scrivere il ciclo a mano. Lo farai più e più volte nel corso della tua carriera di programmatore. Le persone che leggono il tuo codice, incluso te, devono sempre investire tempo, anche se solo pochi secondi, per digerire il significato del loop.

Invece riutilizza una delle librerie disponibili che forniscono codice che fa esattamente questo come StringUtils.repeatda Apache Commons Lang :

StringUtils.repeat(' ', length);

In questo modo, inoltre, non devi preoccuparti delle prestazioni, quindi tutti i dettagli cruenti di StringBuilder, le ottimizzazioni del compilatore ecc. Sono nascosti. Se la funzione si rivelasse così lenta, sarebbe un bug della libreria.

Con Java 11 diventa ancora più semplice:

" ".repeat(length);

5
D'altra parte, se StringUtils non è già utilizzato nel progetto, l'aggiunta di un'altra dipendenza per un'attività così semplice potrebbe essere eccessiva.
Erich Kitzmueller,

1
se si è rivelato lento puoi anche inviare la tua correzione per la funzione.
sarà il


13

Se vuoi solo spazi, che ne dici di:

String spaces = (n==0)?"":String.format("%"+n+"s", "");

che si tradurrà in abs (n) spazi;


6
Ma come va per la velocità? Sono sotto il formato dell'impressione è relativamente lento. Deve analizzare la stringa prima di poterla creare dopo tutto.
C. Ross,

11

da Java 11 :

" ".repeat(10);

da Java 8 :

generate(() -> " ").limit(10).collect(joining());

dove:

import static java.util.stream.Collectors.joining;
import static java.util.stream.Stream.generate;

9

Da Java 11 puoi semplicemente usare String.repeat(count)per risolvere il tuo problema.

Restituisce una stringa il cui valore è la concatenazione di questa stringa ripetuti countvolte.

Se questa stringa è vuota o countè zero, viene restituita la stringa vuota.

Quindi, invece di un ciclo, il codice dovrebbe apparire così:

" ".repeat(length);

8

Penso che questo sia il codice meno possibile, utilizza la classe Guava Joiner:

Joiner .on (""). Join ( Collections.nCopies (10, ""));


La riga di codice più breve ma l'aggiunta di una libreria di grandi dimensioni solo per quella semplice attività non ha senso. Posso creare da solo un JAR più piccolo con un singolo metodo pubic String n(int n) { ... }che consentirà anche un codice "meno": n(10)ma, di nuovo, non ha alcun senso.
Isapir,

@isapir Questa è un'ottima risposta per le persone che stanno già usando Guava
nasch il

1
@nasch Guava non faceva parte della domanda. Ma in ogni caso, nel 2018 non c'è davvero alcun motivo per utilizzare falegname di Guava quando è possibile utilizzare string.join () che è stata aggiunta in Java 8. Vedere stackoverflow.com/a/43011939/968244
isapir

@isapir No, Guava era una risposta. Le risposte su StackOverflow hanno un pubblico più ampio rispetto alla persona che pone la domanda, quindi va bene se alcune risposte non sono le migliori per la persona che le chiede. E il commento su String.join () è fantastico anche se non sono sicuro che sia disponibile su tutte le versioni di Android, che è un uso importante di Java.
nasch,

7

È possibile utilizzare la String.formatfunzione standard per generare N spazi. Per esempio:

String.format("%5c", ' ');

Crea una stringa con 5 spazi.

o

int count = 15;
String fifteenSpacebars = String.format("%" + count + "c", ' ');

Crea una stringa di 15 barre spaziali.

Se si desidera ripetere un altro simbolo, è necessario sostituire gli spazi con il simbolo desiderato:

int count = 7;
char mySymbol = '#';
System.out.println(String.format("%" + count + "c", ' ').replaceAll("\\ ", "\\" + mySymbol));

Produzione:

#######

6

Il mio contributo si basa sull'algoritmo per una rapida esponenziazione.

/**
 * Repeats the given {@link String} n times.
 * 
 * @param str
 *            the {@link String} to repeat.
 * @param n
 *            the repetition count.
 * @throws IllegalArgumentException
 *             when the given repetition count is smaller than zero.
 * @return the given {@link String} repeated n times.
 */
public static String repeat(String str, int n) {
    if (n < 0)
        throw new IllegalArgumentException(
                "the given repetition count is smaller than zero!");
    else if (n == 0)
        return "";
    else if (n == 1)
        return str;
    else if (n % 2 == 0) {
        String s = repeat(str, n / 2);
        return s.concat(s);
    } else
        return str.concat(repeat(str, n - 1));
}

Ho testato l'algoritmo con altri due approcci:

  • Regolare per il ciclo usando String.concat()per concatenare la stringa
  • Regolare per il loop usando a StringBuilder

Codice di prova (concatenazione usando un ciclo for e String.concat()diventa più lento per grandi n, quindi l'ho lasciato fuori dopo la quinta iterazione).

/**
 * Test the string concatenation operation.
 * 
 * @param args
 */
public static void main(String[] args) {
    long startTime;
    String str = " ";

    int n = 1;
    for (int j = 0; j < 9; ++j) {
        n *= 10;
        System.out.format("Performing test with n=%d\n", n);

        startTime = System.currentTimeMillis();
        StringUtil.repeat(str, n);
        System.out
                .format("\tStringUtil.repeat() concatenation performed in    %d milliseconds\n",
                        System.currentTimeMillis() - startTime);

        if (j <5) {
            startTime = System.currentTimeMillis();
            String string = "";
            for (int i = 0; i < n; ++i)
                string = string.concat(str);
            System.out
                    .format("\tString.concat() concatenation performed in        %d milliseconds\n",
                            System.currentTimeMillis() - startTime);
        } else
            System.out
                    .format("\tString.concat() concatenation performed in        x milliseconds\n");
        startTime = System.currentTimeMillis();
        StringBuilder b = new StringBuilder();
        for (int i = 0; i < n; ++i)
            b.append(str);
        b.toString();
        System.out
                .format("\tStringBuilder.append() concatenation performed in %d milliseconds\n",
                        System.currentTimeMillis() - startTime);
    }
}

risultati:

Performing test with n=10
    StringUtil.repeat() concatenation performed in    0 milliseconds
    String.concat() concatenation performed in        0 milliseconds
    StringBuilder.append() concatenation performed in 0 milliseconds
Performing test with n=100
    StringUtil.repeat() concatenation performed in    0 milliseconds
    String.concat() concatenation performed in        1 milliseconds
    StringBuilder.append() concatenation performed in 0 milliseconds
Performing test with n=1000
    StringUtil.repeat() concatenation performed in    0 milliseconds
    String.concat() concatenation performed in        1 milliseconds
    StringBuilder.append() concatenation performed in 1 milliseconds
Performing test with n=10000
    StringUtil.repeat() concatenation performed in    0 milliseconds
    String.concat() concatenation performed in        43 milliseconds
    StringBuilder.append() concatenation performed in 5 milliseconds
Performing test with n=100000
    StringUtil.repeat() concatenation performed in    0 milliseconds
    String.concat() concatenation performed in        1579 milliseconds
    StringBuilder.append() concatenation performed in 1 milliseconds
Performing test with n=1000000
    StringUtil.repeat() concatenation performed in    0 milliseconds
    String.concat() concatenation performed in        x milliseconds
    StringBuilder.append() concatenation performed in 10 milliseconds
Performing test with n=10000000
    StringUtil.repeat() concatenation performed in    7 milliseconds
    String.concat() concatenation performed in        x milliseconds
    StringBuilder.append() concatenation performed in 112 milliseconds
Performing test with n=100000000
    StringUtil.repeat() concatenation performed in    80 milliseconds
    String.concat() concatenation performed in        x milliseconds
    StringBuilder.append() concatenation performed in 1107 milliseconds
Performing test with n=1000000000
    StringUtil.repeat() concatenation performed in    1372 milliseconds
    String.concat() concatenation performed in        x milliseconds
    StringBuilder.append() concatenation performed in 12125 milliseconds

Conclusione:

  • Per grandi n- utilizzare l'approccio ricorsivo
  • Per piccoli n- per loop ha una velocità sufficiente

4
Questa è un'implementazione interessante. Sfortunatamente, contrariamente alle tue conclusioni, è molto inefficiente per n grandi. Sospetto che ciò sia dovuto alle molte allocazioni di memoria che avvengono ogni volta che concatenate le stringhe. Prova a scriverlo come un wrapper attorno a un metodo ricorsivo che utilizza StringBuilder invece di String. Scommetto che scoprirai che i risultati saranno molto migliori.
Klitos Kyriacou,

3
Ci sono molte precauzioni che devi prendere quando esegui un micro-benchmarking di questo tipo, e non penso che tu ne prenda nessuno! Le domande sulle prestazioni dominanti intorno a questo codice potrebbero essere facilmente se è compilato da Hotspot, quanta spazzatura crea, ecc. Scommetto che tutte queste ifaffermazioni rovineranno la previsione del ramo della CPU. Questo dovrebbe davvero essere rifatto usando JMH ( openjdk.java.net/projects/code-tools/jmh ), altrimenti è un po 'inutile.
SusanW,

Nota che questa implementazione ha complessità O (n * log n), mentre StringBuilder è O (n) :)
Leo Leontev,


4

Considerando che abbiamo:

String c = "c"; // character to repeat, for empty it would be " ";
int n = 4; // number of times to repeat
String EMPTY_STRING = ""; // empty string (can be put in utility class)

Java 8 (utilizzando Stream)

String resultOne = IntStream.range(0,n)
   .mapToObj(i->c).collect(Collectors.joining(EMPTY_STRING)); // cccc

Java 8 (utilizzo di nCopies)

String resultTwo = String.join(EMPTY_STRING, Collections.nCopies(n, c)); //cccc

1
Collectors.joining(EMPTY_STRING)è equivalente aCollectors.joining()
PPartisan il

2

RandomStringUtils ha una disposizione per creare una stringa da una determinata dimensione di input. Non posso commentare la velocità, ma è una fodera.

RandomStringUtils.random(5,"\t");

crea un output

\ T \ t \ t \ t \ t

preferibile se non vuoi vedere \ 0 nel tuo codice.



1

Nella maggior parte dei casi hai bisogno solo di stringhe fino a una certa lunghezza, diciamo 100 spazi. È possibile preparare una matrice di stringhe in cui il numero di indice è uguale alla dimensione della stringa riempita di spazio e cercare la stringa, se la lunghezza richiesta è entro i limiti o crearla su richiesta se è al di fuori del limite.



0

Basta sostituire StringBuffer con StringBuilder . Difficile da battere.

Se la tua lunghezza è un numero elevato, potresti implementare qualche autoappendice più efficiente (ma più goffa), duplicando la lunghezza in ogni iterazione:

 public static String dummyString(char c, int len) {
  if( len < 1 ) return "";
  StringBuilder sb = new StringBuilder(len).append(c);
  int remnant = len - sb.length();
  while(remnant  > 0) {
   if( remnant  >= sb.length() ) sb.append(sb);
   else sb.append(sb.subSequence(0, remnant));
   remnant  = len - sb.length();
  }
  return sb.toString();
 }

Inoltre, potresti provare l' Arrays.fill()approccio ( la risposta di FrustratedWithFormsDesigner ).


Puoi nominare alcune delle variabili più di un carattere?
C. Ross,

0

Puoi sostituirlo StringBuffercon StringBuilder(quest'ultimo non è sincronizzato, potrebbe essere più veloce in una singola app thread)

E puoi creare l' StringBuilderistanza una volta, invece di crearla ogni volta che ne hai bisogno.

Qualcosa come questo:

class BuildString {
     private final StringBuilder builder = new StringBuilder();
     public String stringOf( char c , int times ) {

         for( int i = 0 ; i < times ; i++  ) {
             builder.append( c );
         }
         String result = builder.toString();
         builder.delete( 0 , builder.length() -1 );
         return result;
      }

  }

E usalo in questo modo:

 BuildString createA = new BuildString();
 String empty = createA.stringOf( ' ', 10 );

Se tieni la tua createAcome variabile di istanza, puoi risparmiare tempo nella creazione di istanze.

Questo non è sicuro per i thread, se si dispone di più thread, ogni thread dovrebbe avere la propria copia.


0

Per buone prestazioni, combina le risposte di aznilamir e di FrustratedWithFormsDesigner

private static final String BLANKS = "                       ";
private static String getBlankLine( int length )
{
    if( length <= BLANKS.length() )
    {
        return BLANKS.substring( 0, length );
    }
    else
    {
        char[] array = new char[ length ];
        Arrays.fill( array, ' ' );
        return new String( array );
    }
}

Regola le dimensioni in BLANKSbase alle tue esigenze. La mia BLANKSstringa specifica ha una lunghezza di circa 200 caratteri.


0

Avere un metodo come questo. Questo accoda gli spazi richiesti alla fine del dato Stringper fare un dato Stringalla lunghezza di lunghezza specifica.

public static String fillSpaces (String str) {

    // the spaces string should contain spaces exceeding the max needed
    String spaces = "                                                   ";
    return str + spaces.substring(str.length());
}

0

Vuoi che String abbia dimensioni fisse, quindi pad o troncare, per tabulare i dati ...

class Playground {
    private static String fixStrSize(String s, int n) {
        return String.format("%-" + n + "s", String.format("%." + n +"s", s));
    }

    public static void main(String[ ] args) {
        System.out.println("|"+fixStrSize("Hell",8)+"|");
        System.out.println("|"+fixStrSize("Hells Bells Java Smells",8)+"|");
    }
}

|Hell    |
|Hells Be|

Ottimo riferimento qui .


0

Questo ha funzionato per me senza utilizzare alcuna libreria esterna in Java 8

String sampleText = "test"
int n = 3;
String output = String.join("", Collections.nCopies(n, sampleText));
System.out.println(output);

E l'output è

testtesttest

0

int c = 10; Spazi stringa = String.format ("%" + c + "c", ''); questo risolverà il tuo problema.


-1

È anche possibile utilizzare un metodo semplice come di seguito

public static String padString(String str, int leng,char chr) {
        for (int i = str.length(); i <= leng; i++)
            str += chr;
        return str;
    }

Questo è sostanzialmente più lento.
SLaks,

Puoi usare StringBuffer invece della concatenazione di stringhe, nel caso in cui le stringhe con cui hai a che fare siano di grandi dimensioni. Ciò avrà un impatto considerevole sulla velocità
Yashpal Singla,

-2

cosa ne pensi di questo?

public String fillSpaces(int len) {
    /* the spaces string should contain spaces exceeding the max needed */  
    String spaces = "                                                   ";
    return spaces.substring(0,len);
}

EDIT: ho scritto un semplice codice per testare il concetto e qui quello che ho trovato.

Metodo 1: aggiunta di uno spazio singolo in un ciclo:

  public String execLoopSingleSpace(int len){
    StringBuilder sb = new StringBuilder();

    for(int i=0; i < len; i++) {
        sb.append(' ');
    }

    return sb.toString();
  }

Metodo 2: aggiungere 100 spazi e loop, quindi sottostringa:

  public String execLoopHundredSpaces(int len){
    StringBuilder sb = new StringBuilder("          ")
            .append("          ").append("          ").append("          ")
            .append("          ").append("          ").append("          ")
            .append("          ").append("          ").append("          ");

    for (int i=0; i < len/100 ; i++) {
        sb.append("          ")
            .append("          ").append("          ").append("          ")
            .append("          ").append("          ").append("          ")
            .append("          ").append("          ").append("          ");
    }

    return sb.toString().substring(0,len);
  }

Il risultato ottengo creando 12.345.678 spazi:

C:\docs\Projects> java FillSpace 12345678
method 1: append single spaces for 12345678 times. Time taken is **234ms**. Length of String is 12345678
method 2: append 100 spaces for 123456 times. Time taken is **141ms**. Length of String is 12345678
Process java exited with code 0

e per 10.000.000 di spazi:

C:\docs\Projects> java FillSpace 10000000
method 1: append single spaces for 10000000 times. Time taken is **157ms**. Length of String is 10000000
method 2: append 100 spaces for 100000 times. Time taken is **109ms**. Length of String is 10000000
Process java exited with code 0

la combinazione di allocazione diretta e iterazione richiede sempre meno tempo, in media 60 ms in meno quando si creano spazi enormi. Per taglie più piccole, entrambi i risultati sono trascurabili.

Ma per favore continua a commentare :-)


3
@ aznilamir: Hm, come farai per 10k spazi?
Jayan,

L'idea è quella di combinare loop e allocazione diretta di 100 spazi. Ecco i frammenti di codice:
aznilamir

Perché usi più allegati per aggiungere 100 caratteri? Perché non un'appendice da 100 caratteri?
nycynik,

-3

Non conosco alcun metodo integrato per quello che stai chiedendo. Tuttavia, per una lunghezza fissa ridotta come 10, il metodo dovrebbe essere molto veloce.


Se solo fosse una piccola lunghezza fissa come 10.
C. Ross
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.