A maneira mais eficiente de concatenar seqüências de caracteres?


285

Qual é a maneira mais eficiente de concatenar seqüências de caracteres?


9
Quero colocar aqui um aviso de destaque de que a resposta aceita está significativamente incompleta porque não discute todos os casos relevantes.
usr

@usr De fato ... informações mais detalhadas sobre StringBuildercasos de uso podem ser encontradas aqui .
Tamir Vered

Meu novo favorito a partir do C # 6 é $ "Texto constante aqui {foo} e {bar}" ... é como String.Formatem esteróides. O que, em termos de desempenho, é um pouco mais lento em um liners do que +e String.Concat, mas muito melhor do que aqueles, embora mais lento do que StringBuilderem várias chamadas. Na prática, as diferenças de desempenho são tais que, se eu tivesse que escolher apenas uma maneira de concatenar, escolheria interpolações de strings usando $... Se duas maneiras, adicione StringBuilderà minha caixa de ferramentas. Com essas duas maneiras que você está definido.
usar o seguinte comando

A String.Joinresposta abaixo não faz +justiça e é, na prática, uma péssima maneira de concatenar strings, mas é surpreendentemente rápida em termos de desempenho. A resposta porque é interessante. String.Concate String.Joinambos podem atuar em matrizes, mas String.Joinna verdade são mais rápidos. Aparentemente, String.Joiné bastante sofisticado e mais otimizado do que String.Concat, em parte porque opera de maneira semelhante ao StringBuilderque calcula primeiro o comprimento da string e depois constrói a string que se beneficia desse conhecimento usando o UnSafeCharBuffer.
usar o seguinte comando

Ok, então é rápido, mas String.Jointambém requer a construção de uma matriz que parece ineficiente, certo? ... Acontece que, +e de String.Concatqualquer maneira, constrói matrizes para seus constituintes. Conseqüentemente, criar manualmente uma matriz e alimentá-la String.Joiné comparativamente mais rápido ... no entanto, StringBuilderainda supera String.Joinem todos os aspectos práticos, enquanto $é apenas um pouco mais lento e muito mais rápido em longas seqüências de caracteres ... para não mencionar que é complicado e feio de usar String.Joinse você tiver para criar uma matriz para ele no local.
usar o seguinte comando

Respostas:


154

O StringBuilder.Append()método é muito melhor do que usar o +operador. Mas descobri que, ao executar 1000 concatenações ou menos, String.Join()é ainda mais eficiente que StringBuilder.

StringBuilder sb = new StringBuilder();
sb.Append(someString);

O único problema com String.Join é que você precisa concatenar as seqüências com um delimitador comum.

Edit: como apontou @ryanversaw , você pode fazer o delimitador string.Empty.

string key = String.Join("_", new String[] 
{ "Customers_Contacts", customerID, database, SessionID });

11
StringBuildertem um enorme custo inicial comparável, só é eficiente quando usado com cadeias muito grandes ou com muitas concatenações. Não é trivial descobrir qualquer situação. Se o desempenho for um problema, a criação de perfil é seu amigo (consulte ANTS).
Abel

32
Isso não é verdade para concatenação de linha única. Digamos que você faça myString = "foo" + var1 + "bar" + var2 + "olá" + var3 + "world", o compilador transforma isso automaticamente em uma chamada string.concat, que é o mais eficiente possível. Esta resposta é incorreta, há uma abundância de melhores respostas para escolher
csauve

2
Para concatentação trivial de strings, use o que for mais legível. sequência a = b + c + d; quase sempre será mais rápido do que fazê-lo com o StringBuilder, mas a diferença é geralmente irrelevante. Use StringBuilder (ou outra opção de sua escolha) ao adicionar repetidamente à mesma string (por exemplo, criar um relatório) ou ao lidar com strings grandes.
Swanny

5
Por que você não mencionou string.Concat?
Venemo 27/09/13

271

Rico Mariani , o guru do .NET Performance, tinha um artigo sobre esse assunto. Não é tão simples quanto se pode suspeitar. O conselho básico é este:

Se o seu padrão se parecer com:

x = f1(...) + f2(...) + f3(...) + f4(...)

essa é uma concat e é rápida, StringBuilder provavelmente não vai ajudar.

Se o seu padrão se parecer com:

if (...) x += f1(...)
if (...) x += f2(...)
if (...) x += f3(...)
if (...) x += f4(...)

então você provavelmente deseja StringBuilder.

Ainda outro artigo para apoiar essa afirmação vem de Eric Lippert, onde ele descreve as otimizações executadas em +concatenações de uma linha de maneira detalhada.


1
E quanto a String.Format ()?
22815 IronSlug

85

Existem 6 tipos de concatenações de strings:

  1. Usando o +símbolo de mais ( ).
  2. Usando string.Concat().
  3. Usando string.Join().
  4. Usando string.Format().
  5. Usando string.Append().
  6. Usando StringBuilder.

Em um experimento, ficou provado que string.Concat()é a melhor maneira de abordar se as palavras são menores que 1000 (aproximadamente) e se as palavras são maiores que 1000, então StringBuilderdevem ser usadas.

Para mais informações, consulte este site .

string.Join () vs string.Concat ()

O método string.Concat aqui é equivalente à chamada do método string.Join com um separador vazio. Anexar uma string vazia é rápido, mas não fazê-lo é ainda mais rápido, portanto o método string.Concat seria superior aqui.


4
Deve ler que foi provado string.Concat () ou + é a melhor maneira. Sim, posso obter isso no artigo, mas isso me salva em um clique. Então, + e concat compilam no mesmo código.
precisa saber é o seguinte

Usei essa base para tentar tornar um método meu mais eficiente, onde eu só precisava concatenar exatamente três strings. Descobri que +na verdade era 3 milissegundos mais rápido do que string.Concat(), embora eu não tenha examinado a quantidade de strings necessárias antes dos string.Concat()outraces +.
Gnemlock #

59

De Chinh Do - StringBuilder nem sempre é mais rápido :

Regras de ouro

  • Ao concatenar três valores de sequência dinâmica ou menos, use a concatenação tradicional de sequência.

  • Ao concatenar mais de três valores de sequência dinâmica, use StringBuilder.

  • Ao criar uma cadeia grande a partir de vários literais, use o @literal de cadeia ou o operador inline +.

Na maioria das vezes, StringBuilderé a sua melhor aposta, mas há casos, como mostrado nesse post, em que você deve pelo menos pensar em cada situação.


8
afaik @ apenas desativa o processamento de seqüências de escape. msdn.microsoft.com/en-us/library/362314fe.aspx concordar
abatishchev

12

Se você estiver operando em loop, StringBuilderprovavelmente é o caminho a percorrer; economiza a sobrecarga de criar novas seqüências regularmente. No código que será executado apenas uma vez, no entanto,String.Concat provavelmente está bem.

No entanto, Rico Mariani (guru de otimização do .NET) elaborou um questionário no qual afirmou no final que, na maioria dos casos, ele recomenda String.Format.


Há anos que recomendo o uso de string.format sobre string + string para as pessoas com quem trabalhei. Eu acho que as vantagens de legibilidade são uma vantagem adicional além do benefício de desempenho.
Scott Lawrence

1
Esta é a resposta correta real. A resposta atualmente aceita para StringBuilder está incorreta, pois não menciona anexos de linha única para os quais string.concat ou + é mais rápido. Pouco fato conhecido é que o compilador realmente traduz + 's em string.concat. Além disso, para loops ou para vários concats linha eu uso um costume construído construtor corda que só acrescenta ao .ToString é chamado - superar o problema tampão indeterminado que StringBuilder tem
csauve

2
string.Format não é o caminho mais rápido em nenhuma circunstância. Não sei como inventar um caso em que ele vem à frente.
usr

@usr - observe que Rico não está explicitamente dizendo que é o mais rápido , mas apenas a recomendação dele: "Embora seja o pior desempenho e soubéssemos isso com bastante antecedência, ambos os CLR Performance Architects concordam que [string.Format] deveria seja a opção padrão. No caso altamente improvável de que isso se torne um problema de desempenho, o problema é prontamente solucionado com apenas modestas alterações locais. Normalmente, você está apenas aproveitando uma boa manutenção. "
Adam V

@AdamV, a pergunta é sobre o caminho mais rápido. Eu discordo que seja a escolha padrão, embora não por razões de desempenho. Pode ser uma sintaxe desajeitada. O compartilhador pode converter para frente e para trás à vontade.
usr

10

Aqui está o método mais rápido que desenvolvi ao longo de uma década para meu aplicativo PNL em larga escala. Eu tenho variações para IEnumerable<T>e outros tipos de entrada, com e sem separadores de tipos diferentes ( Char, String), mas aqui mostro o caso simples de concatenar todas as strings de uma matriz em uma única string, sem separador. A versão mais recente aqui é desenvolvida e testada em unidade no C # 7 e .NET 4.7 .

Existem duas chaves para maior desempenho; o primeiro é pré-calcular o tamanho total exato necessário. Esta etapa é trivial quando a entrada é uma matriz, como mostrado aqui. Para manipulação IEnumerable<T>, vale a pena reunir primeiro as seqüências de caracteres em uma matriz temporária para calcular esse total (a matriz é necessária para evitar a chamadaToString() mais de uma vez por elemento, pois tecnicamente, dada a possibilidade de efeitos colaterais, isso pode alterar a semântica esperada de uma operação 'junção de cadeia').

Em seguida, dado o tamanho total da alocação da sequência final, o maior aumento no desempenho é obtido com a construção da sequência de resultados no local . Isso exige a técnica (talvez controversa) de suspender temporariamente a imutabilidade de um novo Stringque é inicialmente alocado cheio de zeros. Qualquer controvérsia à parte, no entanto ...

... observe que esta é a única solução de concatenação em massa nesta página que evita inteiramente uma rodada extra de alocação e cópia pelo Stringconstrutor.

Código completo:

/// <summary>
/// Concatenate the strings in 'rg', none of which may be null, into a single String.
/// </summary>
public static unsafe String StringJoin(this String[] rg)
{
    int i;
    if (rg == null || (i = rg.Length) == 0)
        return String.Empty;

    if (i == 1)
        return rg[0];

    String s, t;
    int cch = 0;
    do
        cch += rg[--i].Length;
    while (i > 0);
    if (cch == 0)
        return String.Empty;

    i = rg.Length;
    fixed (Char* _p = (s = new String(default(Char), cch)))
    {
        Char* pDst = _p + cch;
        do
            if ((t = rg[--i]).Length > 0)
                fixed (Char* pSrc = t)
                    memcpy(pDst -= t.Length, pSrc, (UIntPtr)(t.Length << 1));
        while (pDst > _p);
    }
    return s;
}

[DllImport("MSVCR120_CLR0400", CallingConvention = CallingConvention.Cdecl)]
static extern unsafe void* memcpy(void* dest, void* src, UIntPtr cb);

Devo mencionar que este código tem uma ligeira modificação do que eu me uso. No original, chamo a instrução IL cpblk do C # para fazer a cópia real. Por simplicidade e portabilidade no código aqui, substituí-o por P / Invoke memcpy, como você pode ver. Para obter o melhor desempenho em x64 ( mas talvez não em x86 ), convém usar o método cpblk .


string.Joinjá faz todas essas coisas para você. Não há necessidade de escrever você mesmo. Ele calcula o tamanho da sequência final, constrói uma sequência desse tamanho e depois grava na matriz de caracteres subjacente. Ele ainda tem o bônus de usar nomes de variáveis ​​legíveis no processo.
Servy

1
@ Servy Obrigado pelo comentário; de fato String.Joinpode ser eficiente. Como sugeri na introdução, o código aqui é apenas a ilustração mais simples de uma família de funções que utilizo para cenários que String.Joinnão tratam (como otimizar para Charseparador) ou não nas versões anteriores do .NET. Suponho que não deveria ter escolhido isso pelo exemplo mais simples, pois é um caso que String.Joinjá lida bem, embora com a "ineficiência", provavelmente incomensurável, de processar um separador vazio, a saber. String.Empty.
Glenn Slayden

Claro, no caso de você não ter um separador, você deve ligar Concat, o que também faz isso corretamente. De qualquer maneira, você não precisa escrever o código sozinho.
Servy

7
@Servy Comparei o desempenho String.Joinversus o meu código usando este equipamento de teste . Para 10 milhões de operações de concatenação aleatória de até 100 seqüências de caracteres de tamanho cada, o código mostrado acima é consistentemente 34% mais rápido do que String.Joinna versão x64 criada com o .NET 4.7 . Como o OP solicita explicitamente o método "mais eficiente", o resultado sugere que minha resposta se aplica. Se isso abordar suas preocupações, convido você a reconsiderar seu voto negativo.
Glenn Slayden

1
Recentemente, comparei isso com o x64 full CLR 4.7.1 e acho que ele é duas vezes mais rápido que uma string. Entre com cerca de 25% menos memória alocada ( i.imgur.com/SxIpEmL.png ) ao usar cpblk ou github.com/ JonHanna / Mnemosyne
-starin

6

Neste artigo do MSDN :

Há alguma sobrecarga associada à criação de um objeto StringBuilder, tanto em tempo quanto em memória. Em uma máquina com memória rápida, um StringBuilder vale a pena se você estiver executando cerca de cinco operações. Como regra geral, eu diria que 10 ou mais operações de cadeia são uma justificativa para a sobrecarga em qualquer máquina, mesmo que seja mais lenta.

Portanto, se você confia no MSDN, vá com o StringBuilder se precisar executar mais de 10 operações / concatenações de strings - caso contrário, concatenar simples com o '+' é bom.



5

Além das outras respostas, lembre-se de que o StringBuilder pode receber uma quantidade inicial de memória para alocar .

A capacidade de parâmetro define o número máximo de caracteres que podem ser armazenadas na memória alocada pela instância atual. Seu valor é atribuído à propriedade Capacity . Se o número de caracteres a serem armazenados na instância atual exceder esse valor de capacidade , o objeto StringBuilder alocará memória adicional para armazená-los.

Se a capacidade for zero, a capacidade padrão específica da implementação será usada.

Anexar repetidamente a um StringBuilder que não foi pré-alocado pode resultar em muitas alocações desnecessárias, assim como concatenar repetidamente seqüências regulares.

Se você sabe quanto tempo a string final será, pode calculá-la trivialmente ou pode fazer um palpite sobre o caso comum (alocar demais não é necessariamente uma coisa ruim), você deve fornecer essas informações ao construtor ou ao Propriedade de capacidade . Especialmente ao executar testes de desempenho para comparar o StringBuilder com outros métodos, como o String.Concat, que fazem a mesma coisa internamente. Qualquer teste que você vê online que não inclui a pré-alocação do StringBuilder em suas comparações está errado.

Se você não consegue adivinhar o tamanho, provavelmente está escrevendo uma função de utilitário que deve ter seu próprio argumento opcional para controlar a pré-alocação.


4

A seguir, pode ser mais uma solução alternativa para concatenar várias seqüências de caracteres.

String str1 = "sometext";
string str2 = "some other text";

string afterConcate = $"{str1}{str2}";

interpolação de string


1
Isso é realmente surpreendente como um método geral de concatenação. É basicamente, String.Formatmas mais legível e fácil de trabalhar. Marcando-o como benchmark, é um pouco mais lento que +e String.Concatem uma linha de concatenações, mas muito melhor do que aqueles em chamadas repetidas, tornando StringBuildermenos necessário.
usar o seguinte comando

2

O mais eficiente é usar o StringBuilder, assim:

StringBuilder sb = new StringBuilder();
sb.Append("string1");
sb.Append("string2");
...etc...
String strResult = sb.ToString();

@jonezy: String.Concat é bom se você tiver algumas coisas pequenas. Mas se você estiver concatenando megabytes de dados, é provável que o seu programa seja tanque.


ei, qual é a solução para megabytes de dados?
Neel

2

Experimente estes 2 pedaços de código e você encontrará a solução.

 static void Main(string[] args)
    {
        StringBuilder s = new StringBuilder();
        for (int i = 0; i < 10000000; i++)
        {
            s.Append( i.ToString());
        }
        Console.Write("End");
        Console.Read();
    }

Vs

static void Main(string[] args)
    {
        string s = "";
        for (int i = 0; i < 10000000; i++)
        {
            s += i.ToString();
        }
        Console.Write("End");
        Console.Read();
    }

Você descobrirá que o 1º código terminará muito rápido e a memória estará em boa quantidade.

O segundo código talvez a memória esteja ok, mas levará mais tempo ... muito mais tempo. Portanto, se você tem um aplicativo para muitos usuários e precisa de velocidade, use o primeiro. Se você tem um aplicativo para um aplicativo de um usuário por um período curto, talvez possa usar os dois ou o segundo será mais "natural" para os desenvolvedores.

Felicidades.


1

Por apenas duas strings, você definitivamente não deseja usar o StringBuilder. Há um limite acima do qual a sobrecarga de StringBuilder é menor que a sobrecarga de alocar várias seqüências.

Portanto, para mais de 2 a 3 cordas, use o código do DannySmurf . Caso contrário, basta usar o operador +.


1

System.String é imutável. Quando modificamos o valor de uma variável de cadeia, uma nova memória é alocada para o novo valor e a alocação de memória anterior liberada. O System.StringBuilder foi projetado para ter o conceito de uma sequência mutável, na qual uma variedade de operações pode ser executada sem a alocação de um local de memória separado para a sequência modificada.


1

Outra solução:

dentro do loop, use List em vez de string.

List<string> lst= new List<string>();

for(int i=0; i<100000; i++){
    ...........
    lst.Add(...);
}
return String.Join("", lst.ToArray());;

é muito muito rápido.



0

Isso dependeria do código. O StringBuilder é mais eficiente em geral, mas se você estiver concatenando apenas algumas seqüências e fazendo tudo em uma linha, as otimizações de código provavelmente cuidarão disso para você. É importante pensar também na aparência do código: para conjuntos maiores, o StringBuilder facilitará a leitura, para conjuntos pequenos, o StringBuilder apenas adiciona desordem desnecessária.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.