C# Tips & Tricks - StringBuilder

Esse é um assunto já um tanto quanto conhecido da maioria dos desenvolvedores mas acho interessante registrar aqui pois ainda vejo muita gente concatenando strings sem perceber que existem outras alternativas menos custosas.

A classe System.String representa textos como uma coleção de caracteres Unicode. Um objeto String é então uma coleção sequencial de objetos do tipo System.Char que representam a string.

Uma característica que alguns não conhecem é que a string é imutável, ou seja, o seu valor não pode ser modificado (read-only). Verifique que a maioria dos métodos existentes na classe System.String retornam uma nova string e não modificam a instância da mesma.

Quando concatenamos a string com uma outra string, por exemplo...

string str1 = "a";
str1 = str1 + "b";

uma nova instância da classe string é criada e atribuída a variável str1. Isso faz com que códigos que concatenam strings extensivamente utilizem muita memória e consequentemente tenham uma perda de performance.

A classe System.Text.StringBuilder representa então uma string mutável de caracteres e representa um ganho de performance enorme se utilizada em nosso código.

Vou demonstrar abaixo dois códigos que podem ser utilizados em uma aplicação Console e que irão demonstrar a diferença de performance quando utilizamos StringBuilder. Para realizar a medição do tempo utilizaremos a classe System.Diagnostics.Stopwatch.

Digamos então que temos o seguinte código:

using System;
using System.Text;
using System.Diagnostics;

namespace CSharpStringBuilderPerformance
{
    // Main program
    class Program
    {
        static void Main(string[] args)
        {
            string suaString = "";

            // cria StopWatch
            Stopwatch stopWatch = new Stopwatch();

            // inicia medição do tempo
            stopWatch.Start();

            // loop para teste
            for (int i = 0; i < 10000; ++i)
                suaString += "Número: " + i.ToString() + "\n";

            // finaliza medição do tempo
            stopWatch.Stop();

            // tempo decorrido
            Console.WriteLine(stopWatch.ElapsedMilliseconds);
        }
    }
}

O que o código faz é bem simples, somente cria uma variável chamada suaString e com um loop de 0 a 9999 realiza concatenação da string "Número: (contador)" seguido de uma nova linha.

Os métodos Start e Stop do objeto da classe Stopwatch realizam respectivamente o início da medida do tempo e o final da medida do tempo.

Realizando o processamento do código acima 10 vezes tive os seguintes resultados computados em milisegundos:

1728
1708
1720
1712
1709
1726
1717
1700
1731
1702

Obs: Dependendo do seu hardware você pode ter valores diferentes (óbvio).

Ok, agora vamos utilizar a classe StringBuilder e o método AppendLine da mesma para concatenar a string. Vou somente colocar o código do método Main para simplificar melhor o exemplo.

static void Main(string[] args)
{
    // cria StringBuilder
    StringBuilder stringBuilder = new StringBuilder();

    // cria StopWatch
    Stopwatch stopWatch = new Stopwatch();

    // inicia medição do tempo
    stopWatch.Start();

    // loop para teste
    for (int i = 0; i < 10000; ++i)
        stringBuilder.AppendLine("Número: " + i.ToString());

    // finaliza medição do tempo
    stopWatch.Stop();

    // tempo decorrido
    Console.WriteLine(stopWatch.ElapsedMilliseconds);
}

A idéia é substituir o objeto string por um objeto StringBuilder e utilizar o método AppendLine. Se existir a necessidade de transformar o StringBuilder em String novamente é só utilizarmos o método ToString().

Processando o código com StringBuilder 10 vezes temos o incrível resultado abaixo:

6
5
7
6
5
8
9
10
9
6

Perceberam a diferença de performance? Portanto utilizem o StringBuilder sempre que possível.

Alguns links de ajuda:

String Class
http://bit.ly/6ebptP

StringBuilder Class
http://bit.ly/64pDlE

Stopwatch Class
http://bit.ly/8TcpCG

Um comentário: