Maneira simples de repetir uma String em java

598

Estou procurando por um método comum comum ou operador que me permita repetir algumas String n vezes. Eu sei que poderia escrever isso usando um loop for, mas desejo evitar loops sempre que necessário e um método direto simples deve existir em algum lugar.

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

repeated.equals("abcabcabc");

Relacionado a:

repetir string javascript Crie NSString repetindo outra string um determinado número de vezes

Editado

Eu tento evitar loops quando eles não são completamente necessários porque:

  1. Eles aumentam o número de linhas de código, mesmo que estejam escondidas em outra função.

  2. Alguém lendo meu código precisa descobrir o que estou fazendo nesse loop for. Mesmo que seja comentado e possua nomes significativos de variáveis, eles ainda precisam garantir que não esteja fazendo nada "inteligente".

  3. Os programadores adoram colocar coisas inteligentes em loops, mesmo que eu escreva para "apenas fazer o que se pretende fazer", isso não impede que alguém apareça e adicione alguma "correção" inteligente.

  4. Muitas vezes, é fácil errar. Para loops envolvendo índices tendem a gerar um erro.

  5. Os loops geralmente reutilizam as mesmas variáveis, aumentando a chance de encontrar bugs de escopo realmente difíceis.

  6. Para loops, aumente o número de lugares que um caçador de insetos precisa procurar.

Ethan Heilman
fonte
35
Entendo que os loops podem causar alguns problemas reais. Mas você não deve tentar evitar loops "a todo custo", porque se isso lhe custa legibilidade, capacidade de manutenção e velocidade, você está sendo contraproducente. Este é um daqueles casos.
Imagist
9
"Eles aumentam o número de linhas de código, mesmo se estiverem escondidos em outra função" ... uau, apenas uau. Big-O, não LoC
Pyrolistical
7
@ imagist Estou evitando loops em situações em que isso me custa legibilidade e manutenção. Considero a velocidade como a questão menos importante aqui (de fato, não é uma questão). Acho que os loops estão em uso excessivo e estou tentando aprender a usá-los apenas quando forem necessários e não como uma solução padrão.
6139 Ethan Heilman
3
@Pyrolistical Não estou reivindicando desempenho ou benefícios assintóticos. Em vez de dizer que, escrevendo menos código e usando as funções da biblioteca, em vez de reinventar a roda, reduzo a área de superfície do erro (linhas de código) enquanto aumenta a legibilidade. As duas coisas boas, tenho certeza que você concorda.
6139 Ethan Heilman
4
@ e5; desculpe-me por publicar anos mais tarde. Acho esta pergunta muito apropriada. Se inserido em um método, argumentos devem ser testados (tempos> = 0), erros gerados etc. Isso adiciona robustez, mas também linhas de código para leitura. Repetir uma string é algo inequívoco. Quem lê o código sabe exatamente o que uma string faz.repeat, mesmo sem uma linha de comentário ou javadoc. AINDA introduz alguma forma de verificação de "robustez" com a qual precisamos nos preocupar. Se eu pudesse pedir 10 melhorias, isso (mais ou menos) as coisas seriam uma.
AgostinoX

Respostas:

230

String::repeat

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

Novo no Java 11 é o método String::repeatque faz exatamente o que você solicitou:

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

Seu Javadoc diz:

/**
 * 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
fonte
2
Código-fonte @Nicolai para ele, apenas no caso de alguém cuidados hg.openjdk.java.net/jdk/jdk/file/fc16b5f193c7/src/java.base/...
Eugene
7
Eu não vi mesmo java 9 ainda na rua (e não vai para um longo tempo ..) - e 11 é aparentemente definido para navio ..
javadba
1
Provavelmente óbvio, mas você também pode chamar esse método em uma string literal:"abc".repeat(3)
Boann
895

Aqui está a versão mais curta (é necessário o Java 1.5 ou superior):

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

Onde né o número de vezes que você deseja repetir a sequência e sé a sequência a ser repetida.

Não são necessárias importações ou bibliotecas.

user102008
fonte
22
Eu não acho que esteja ofuscado. Tipos primitivos ( char[]neste caso) são instanciados com nulos, então a Stringé criado a partir de char[]e os nulos estão replaced()com o caractere em que você desejas
Amarok
32
Enquanto isso é muito inteligente (+1) Eu acho que praticamente prova o ponto que para loops muitas vezes fazem para código mais claro
Richard Tingle
75
Para quem reclama de ofuscação, a legibilidade depende da alfabetização. Isso é perfeitamente claro no que faz e educativo para aqueles que podem não vê-lo imediatamente. A propósito, é isso que você obtém com Java.
dansalmo
11
Para um melhor desempenho, ...replace('\0', str)deve ser usado em vez da versão String.
user686249
11
@ user686249: Há apenas replace(char oldChar, char newChar)e replace(CharSequence target, CharSequence replacement)então eu não vejo como isso poderia funcionar
user102008
334

Se você estiver usando Java <= 7 , isso é tão "conciso" quanto possível:

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

No Java 8 e acima, há uma maneira mais legível:

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

Finalmente, para Java 11 e superior, existe um novo repeat​(int count)método específico para isso ( link )

"abc".repeat(12);

Como alternativa, se o seu projeto usa bibliotecas java, há mais opções.

Para o Apache Commons :

StringUtils.repeat("abc", 12);

Para o Google Guava :

Strings.repeat("abc", 12);
Caner
fonte
4
O primeiro causa uma exceção quando né zero.
precisa saber é o seguinte
O exemplo java 8 não compilar -> Incompatibilidade de tipo: não é possível converter de List <Character> para CharSequence
Arigion
6
@Arigion stem que ser String, não umChar
Caner
@ Caner Obrigado. Meu mal, peço desculpas. Aparentemente, eu estava muito cansado ontem. Desculpe pela redução de votos. Vou remover o downvote assim que eu poderia (é bloqueado até que pergunta é editado)
Arigion
@Arigion nenhum problema, deixou claro agora que s é uma string
Caner
312

Commons Lang StringUtils.repeat ()

Uso:

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

repeated.equals("abcabcabc");
ChssPly76
fonte
99
usando um único método de dependência para o bem da simplicidade, a longo prazo pode resultando em um frasco-inferno
dfa
81
Claro, exceto que é comum. Eu acho que nunca vi um projeto com mais de 5000 LOCS que não tinha commons lang.
Ethan Heilman
11
O Commons Lang é de código aberto - faça o download e dê uma olhada. Claro que tem um loop interno, mas não é tão simples assim. Muito esforço foi feito na criação de perfil e otimização dessa implementação.
ChssPly76 5/08/09
28
Não evito loops por motivo de desempenho (leia meus motivos na pergunta). Quando alguém vê StringUtils.repeat, sabe o que estou fazendo. Eles não precisam se preocupar que eu tentei escrever minha própria versão do repeat e cometi um erro. É uma unidade cognitiva atômica!
Ethan Heilman
7
@ Thorbjørn Ravn Andersen - ele pode ficar muito mais interessante se as coisas continuarem a ser tomadas fora de contexto
ChssPly76
140

O Java 8 String.joinfornece uma maneira organizada de fazer isso em conjunto com Collections.nCopies:

// say hello 100 times
System.out.println(String.join("", Collections.nCopies(100, "hello")));
Boann
fonte
7
obrigado! Para o Android, TextUtils.join () pode ser usado em vez de String.join () #
MinaHany
2
Obrigado por esta resposta. Parece ser a maneira mais limpa, sem usar nenhum método de utilitário externo de API oder! muito bom!!
Andreas M. Oberheim
2
O bom desse método é que, com o join, você pode fornecer um caractere separador que será muito útil se você estiver, por exemplo, criando uma lista CSV. Com todos os outros métodos, você tem um caractere de junção final que precisa ser removido em uma operação separada.
DroidOS
102

Aqui está uma maneira de fazer isso usando apenas funções String padrão e sem loops explícitos:

// create a string made up of  n  copies of  s
repeated = String.format(String.format("%%%ds", n), " ").replace(" ",s);
IJ Kennedy
fonte
5
Incrível :-) Embora tenha cuidado com n se tornar zero…!
Yang Meyer
4
@Vijay Dev & fortran: Não, ele quis dizer replace(). No Java 1.5+, há uma versão sobrecarregada replace()que leva dois CharSequences (incluindo Strings): download.oracle.com/javase/1.5.0/docs/api/java/lang/…
user102008
79
Ai. Isso é feio.
Karel Bílek 22/03
8
@mzuba, digamos n=3: primeiro formata uma string para se parecer com algo %03d( %%é escapar do sinal de porcentagem), que é o código de formatação para adicionar 3 zeros de preenchimento, depois formata 0com isso, levando a 000, e finalmente substituindo cada um 0com as strings
fortran
15
Você pode fazer a solução menos feio e mais fácil de entender: String.format ( "% 0" + n + "d", 0) .replace ( "0", s)
Artur
87

Se você é como eu e quer usar o Google Guava e não o Apache Commons. Você pode usar o método repeat na classe Guava Strings.

Strings.repeat("-", 60);
Jack
fonte
2
... e obtenha 3Mb de novas dependências.
MonoThreaded
6
@MonoThreaded Eu pensei que seria óbvio, mas não inclua goiaba apenas para repetir a string. Minha resposta foi sobre se você já estiver usando a goiaba de qualquer maneira, é assim que você faria.
25318 Jack
53

Com , você também pode usar Stream.generate.

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

e você pode envolvê-lo em um método utilitário simples, se necessário:

public static String repeat(String str, int times) {
   return Stream.generate(() -> str).limit(times).collect(joining());
}
Alexis C.
fonte
6
... ou return IntStream.range(0, times).mapToObj(i -> str).collect(joining());que parallelizes melhor
Alexis C.
32

Então você quer evitar loops?

Aqui você tem:

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

(é claro que sei que isso é feio e ineficiente, mas não possui loops :-p)

Você quer isso mais simples e mais bonito? use jython:

s * 3

Edit : vamos otimizar um pouco :-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 : Eu fiz um benchmark rápido e sujo para as 4 principais alternativas, mas não tenho tempo para executá-lo várias vezes para obter os meios e plotar os tempos para várias entradas ... Então, aqui está o código, se alguém quiser experimentá-lo:

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

São necessários dois argumentos, o primeiro é o número de iterações (cada função é executada com tempos de repetição arg de 1..n) e o segundo é a sequência a ser repetida.

Até agora, uma rápida inspeção dos tempos de execução com entradas diferentes deixa o ranking mais ou menos assim (melhor para pior):

  1. O StringBuilder iterativo acrescenta (1x).
  2. Invocações de log2 de concatenação recursiva (~ 3x).
  3. Invocações lineares de concatenação recursiva (~ 30x).
  4. Concatenação iterativa linear (~ 45x).

Eu nunca imaginei que a função recursiva fosse mais rápida que o forloop: -o

Divirta-se (xD ctional).

fortran
fonte
1
+1 por recursão e, obviamente, por ser um hacker lisp. Também não acho que isso seja ineficiente, a concatenação de strings não é o tempo de guerra que era antes, porque + realmente é apenas um UTH de stringBuilder. Consulte stackoverflow.com/questions/47605/java-string-concatenation e schneide.wordpress.com/2009/02/23/… . Eu me pergunto quanto todas essas pilhas empurram e saem do custo da recursão ou se o hotspot cuida delas. Realmente gostaria de ter tempo livre para compará-lo. Alguém mais, talvez?
6139 Ethan Heilman
@ e5: fortran está certo; essa solução poderia ser mais eficiente. Esta implementação criará desnecessariamente um novo StringBuilder (e um novo String) para cada recursão. Ainda é uma boa solução.
Rob
3
@ e5 eu desejo que eu era um hacker Lisp xD ... Se eu fosse, eu teria usado um rabo recursiva função :-P
fortran
1
Microbenchmarks não funcionam bem em Java. Tentar medir a velocidade de suas implementações assim não é bom.
ceklock
@tecnotron eu sei, mas ainda assim eles são melhores que nada ... E a única 'surpresa' foi a pequena diferença entre a concatenação de loop ingênuo e a recursão linear.
fortran
20

Este contém menos caracteres que a sua pergunta

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();
}
Pyrolistical
fonte
4
Ele contém mais caracteres do que minha resposta StringUtils.repeat (str, n).
Ethan Heilman
8
A menos que você já está usando Apache Commons, esta resposta é muito menos problemas - não download outra biblioteca, incluindo-a no seu classpath, certificando-se sua licença é compatível com o seu, etc.
Paul Tomblin
7
Por favor, nunca retorne nulo - nesse caso, retorne uma string vazia, permitindo que você sempre use o valor retornado desmarcado. Caso contrário, o que eu recomendaria para o pôster.
Thorbjørn Ravn Andersen
7
Bem, existem três maneiras de lidar se s é nulo. 1. Passe o erro (retornar nulo), 2. Esconda o erro (retorne ""), 3. Lance um NPE. Ocultar o erro e lançar um NPE não são legais, então eu passei o erro.
Pyrolistical
1
@ EthanHeilman adicione os 2 MB de valor commons-lang3.3.1-sourcese você não é mais tão bom;) Mas se alguém já tiver commons-lang, eu apoio sua resposta.
TWIStErRob
9

com base na resposta do fortran , esta é uma versão recusativa que usa um StringBuilder:

public static void repeat(StringBuilder stringBuilder, String s, int times) {
    if (times > 0) {
        repeat(stringBuilder.append(s), s, times - 1);
    }
}

public static String repeat(String s, int times) {
    StringBuilder stringBuilder = new StringBuilder(s.length() * times);
    repeat(stringBuilder, s, times);
    return stringBuilder.toString();
}
dfa
fonte
1
o loop ao invés de recursão reduziria o número de quadros de pilha para um grande número de repetições.
La-comadreja
7

usar Dollar é simples como digitar:

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

PS: repeat também funciona para array, List, Set, etc

dfa
fonte
3
o método assertThat () é realmente necessário?
ceklock
7

Eu queria uma função para criar uma lista delimitada por vírgula de pontos de interrogação para fins de JDBC e encontrei esta postagem. Então, eu decidi pegar duas variantes e ver qual tinha melhor desempenho. Após 1 milhão de iterações, o StringBuilder, da variedade de jardins, levou 2 segundos (fun1) e a versão supostamente mais ideal (fun2) levou 30 segundos. Qual o sentido de ser enigmático de novo?

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);
}
Prumo
fonte
3
Faz sentido que o segundo demore muito mais. Ele está executando uma pesquisa de sequência e modificando a sequência de caracteres por caractere.
Ethan Heilman
7

Solução OOP

Quase todas as respostas propõem uma função estática como uma solução, mas pensando em Orientação a Objetos (para fins de reutilização e clareza), eu vim com uma Solução via Delegação através da Interface CharSequence (que também abre a usabilidade em Classes CharSequence mutáveis).

A classe a seguir pode ser usada com ou sem Separator-String / CharSequence e cada chamada para "toString ()" cria a String final repetida. O Input / Separator não se limita apenas à String-Class, mas pode ser toda classe que implementa CharSequence (por exemplo, StringBuilder, StringBuffer, etc)!

Código fonte:

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

Exemplo de uso:

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

Saída de exemplo:

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
fonte
4
raramente tenho visto coisas tão repugnantes: /
Ven
6

usando apenas classes JRE ( System.arraycopy ) e tentando minimizar o número de objetos temporários, você pode escrever algo como:

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

EDITAR

e sem loops você pode tentar com:

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

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

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

EDIT 2

o uso de coleções é ainda mais curto:

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

no entanto, eu ainda gosto da primeira versão.

dfa
fonte
6
-1: muito inteligente pela metade. Se seu objetivo é tornar o código legível ou eficiente, essas "soluções" não são uma boa idéia. 'repeat' poderia simplesmente ser reescrito usando um StringBuilder (configurando a capacidade inicial). E 'repeat2' / 'repeat3' são realmente ineficientes e dependem da sintaxe não especificada da String produzida por String []. ToString ().
Stephen C
@Thorb: absolutamente, com este código que você não pode usar "metacharacter", [],
dfa
@ Stephen: a pergunta foi editada para solicitar explicitamente sem loops. Resposta baseada Um StringBuilder já foi fornecido para evitar dirigir para deixar um duplicado
dfa
@ Stephan: Eu não consigo descobrir o voto negativo. Minha resposta editada é livre de loop, conforme solicitado. Não há pedidos sobre eficiência. Penso que esta questão é apenas um esforço intelectual para produzir uma concatenação sem loop.
Dfa
@Stephan: String produzida via Collection.toString (e Arrays.toString) estão claramente especificadas em AbstractCollection.toString: "A representação da string consiste em uma lista dos elementos da coleção na ordem em que são retornados pelo iterador, entre colchetes ( "[]"). Os elementos adjacentes são separados pelos caracteres "," (vírgula e espaço). "
Dfa
6

Não é o mais curto, mas (acho) a maneira mais rápida é usar o 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
fonte
5

Se a sua preocupação é a velocidade, você deve usar o mínimo possível de cópias em memória. Portanto, é necessário trabalhar com matrizes de caracteres.

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

Para testar a velocidade, um método ideal semelhante usando o StirngBuilder é o seguinte:

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 o código para testá-lo:

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 aqui a execução resulta do meu sistema:

elapsed repeatString: 6006571
elapsed repeatStringSB: 9064937

Observe que o teste para o loop é acionar o JIT e obter melhores resultados.

Panayotis
fonte
4

por questões de legibilidade e portabilidade:

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

Se você está preocupado com o desempenho, basta usar um StringBuilder dentro do loop e executar um .toString () na saída do loop. Caramba, escreva sua própria Util Class e reutilize-a. 5 linhas de código máx.

WolfmanDragon
fonte
2

Eu realmente gosto dessa pergunta. Há muito conhecimento e estilos. Então eu não posso sair sem mostrar meu 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);
    }
}

Você gosta disso?

Daniel De León
fonte
1
No meu teste mais extremo, eu produzir um 1,7 bilhões (1,7 gigas) comprimento da corda repetição ,, 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);
}
falcão
fonte
2

Loop simples

public static String repeat(String string, int times) {
    StringBuilder out = new StringBuilder();
    while (times-- > 0) {
        out.append(string);
    }
    return out.toString();
}
Codler
fonte
2
passe timespara o construtor StringBuilder.
Behrouz.M
2

Tente isto:

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

fonte
2

Usando recursão, você pode fazer o seguinte (usando operadores ternários, uma linha no máximo):

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

Eu sei, é feio e provavelmente não é eficiente, mas é uma linha!

HyperNeutrino
fonte
Essa é a abordagem que eu adotaria, mas por que fazer mais verificações do que o necessário? número de retorno> 0? sequência + repetição (sequência, número-1): "";
Fering 7/12
Ah, parece que niczm25 respondeu com ele abaixo #
Fering 07/12/18
@ Principal motivo para que desta maneira seja O (log N) médio e não O (N) sempre. Um pouco mais de otimização que a outra, embora ainda seja ruim.
HyperNeutrino
2

uma solução simples de uma linha:
requer Java 8

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

Apesar do seu desejo de não usar loops, acho que você deve usar um 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();
}

Seus motivos para não usar um loop for não são bons. Em resposta às suas críticas:

  1. Qualquer que seja a solução usada, certamente será mais longa que isso. O uso de uma função pré-criada apenas a coloca em mais capas.
  2. Alguém que esteja lendo seu código terá que descobrir o que você está fazendo nesse loop não for. Dado que um loop for é a maneira idiomática de fazer isso, seria muito mais fácil descobrir se você o fizesse com um loop for.
  3. Sim, alguém pode adicionar algo inteligente, mas evitando um loop for, você está fazendo algo inteligente . É como dar um tiro no pé intencionalmente para evitar dar um tiro no pé por acidente.
  4. Erros de um por um também são incrivelmente fáceis de detectar com um único teste. Como você deve testar seu código, um erro de um por um deve ser fácil de corrigir e capturar. E vale a pena notar: o código acima não contém um erro de um por um. Pois loops são igualmente fáceis de acertar.
  5. Portanto, não reutilize variáveis. Isso não é culpa do loop for.
  6. Novamente, o mesmo acontece com qualquer solução que você usar. E como eu observei antes; um caçador de bugs provavelmente estará esperando que você faça isso com um loop for, para que seja mais fácil encontrá-lo se você usar um loop for.
Imagist
fonte
3
-1. Aqui estão dois exercícios para você: a) execute seu código com repetitions = -5. b) faça o download do Commons Lang e execute repeatString('a', 1000)um milhão de vezes seguidas; faça o mesmo com o seu código; compare os tempos. Para crédito extra, faça o mesmo com repeatString('ab', 1000).
ChssPly76 5/08/09
2
Você está argumentando que seu código é mais legível StringUtils.repeat("ab",1000)? Porque essa foi a minha resposta que você votou mal. Ele também tem um desempenho melhor e não possui bugs.
ChssPly76 5/08/09
2
Leia a segunda frase da pergunta que você está citando. "Eu tento evitar loops a todo custo porque" foi adicionado à pergunta como um esclarecimento em resposta à resposta de Andrew Hare após a minha resposta - não que isso importe porque se a posição que você está assumindo é "a resposta é ruim se o loop for usado em qualquer lugar ", não há respostas para a pergunta do OP. Até as soluções da dfa - por mais inventivas que sejam - são usadas para loops no interior. "jar hell" foi respondido acima; O commons lang é usado em qualquer aplicativo de tamanho decente de qualquer maneira e, portanto, não adiciona uma nova dependência.
ChssPly76 5/08/09
2
@ ChssPly76 neste momento, tenho certeza que o imagist está trollando. Eu realmente tenho dificuldade em ver como alguém pode ler o que escrevi e pensar seriamente nas respostas digitadas acima.
6139 Ethan Heilman
1
@ ChssPly76 minhas respostas não têm quaisquer laços de todo :-P
fortran
1

Se você souber apenas o comprimento da sequência de saída (e ela pode não ser divisível pelo comprimento da sequência de entrada), use este método:

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

Demonstração de uso:

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

Não use com vazio se length> 0, pois é impossível obter o resultado desejado nesse caso.

John McClane
fonte
0

aqui está o último 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();
    }
    }

ele nem precisa ser tão grande, pode ser transformado nisso e pode ser copiado e colado em uma classe de utilitário em seu projeto.

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

Então, e5, acho que a melhor maneira de fazer isso seria simplesmente usar o código mencionado acima, ou qualquer uma das respostas aqui. mas commons lang é muito grande se for um projeto pequeno

alexmherrmann
fonte
Eu não acho que há muito mais que você possa fazer ... excitar talvez um AOT !!
Alexmherrmann 11/08
0

Eu criei um método recursivo que faz a mesma coisa que você quer .. fique à vontade para usar isso ...

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

Eu tenho a mesma resposta em Posso multiplicar seqüências de caracteres em java para repetir seqüências?

niczm25
fonte
Realocação desnecessária de strings e sobrecarga de recursão ... ruim, ruim, não boa.
Alexander - Restabelecer Monica
1
Isso será lento. Não recomendado! Use em StringBuildervez disso.
Svetlin Nakov 30/10
0
public static String rep(int a,String k)

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

Você pode usar esse método recursivo para o objetivo desejado.

Syed Salman Hassan
fonte