Makale Özeti

String.concat ?? + operatörü ?? stringbuilder sınıfı ?? Nerede hangi yöntemi nasıl kullanmalıyız?

Makale

String birleştirme işleminde dikkat edilmesi gerekenler

Tüm yazılımcı arkadaşlara selamlar, 

Bu yazımda string birleştirirken dikkat etmeniz gereken performansı etkileyen çok önemli bir konuya değineceğim.

Geliştirdiğimiz uygulamalarda string tipini kullanmaktayız, ve çoğu zaman kurduğumuz algoritmalarda string değişkenlerini birbirine ekleriz.
Örnek vermek gerekirse ;
Bir server uygulamasının client uygulamamıza network üzerinden bir metin dosyası gönderdiğini varsayalım.
Aşağıdaki kodu inceleyelim;

TcpClient tclA=new TcpClient();
tclA.Connect("localhost",80);
NetworkStream nstA=new NetworkStream(tclA);
StreamReader strA=new StreamReader(nstA);
StreamWriter stwA=new StreamWriter(nstA);
string strIncomingData="";

while (nstA.DataAvailable)
{
	strIncomingData+=strA.ReadLine();
}

Network üzerinden gelen bilgi “strIncomingData+=strA.ReadLine()” komutu ile depolanıyor.

Ufak boyutlu dosyalarda bu işlem çok sağlıklı çalışacaktır.
Peki büyük boyutlu dosyalarda neden problem çıkartır?

Sebebi CLR’nin string tipini hafızanın HEAP alanında saklaması ve bu tipin yeni değerinin atanması işleminde herseferinde HEAP bölgesinde yeni bir adres açması ve eski adresi tutmasıdır.

Evet her "strIncomingData+=strA.ReadLine()" komutunda belleğin heap alanında yeni bir adres açılır ve bir önceki heap alanı bırakılır.
Bu ufak döngülerde veya ufak çaplı dosyalarda problem yaratmayacak fakat örneğimizdeki gibi bir dosya transfer programında performans problemleri yaratacaktır.

Peki nasıl olacakta HEAP'i doldurmayacağız? .NET framework'ü bize stringbuider sınıfını çözüm olarak sunmaktadır.
StringBuilder sınıfı System.Text isimalanında bulunmaktadır.

Aşağıdaki kod örneğini çalıştırdığınızda size 2 yöntemden birini seçmenizi isteyecektir.
1 girdiğinizde size ilk anlattığım standart yöntem ile 100.000 defa strA değişkenine herseferinde A harfini ekleyecektir.
Tabi bu arada işlemin başlangıç ve bitiş saati ekrana yazılacaktır.
2 yi seçtiğinizde ise aynı işlemi StringBuilder sınıfını kullanarak yapacaktır.Bu sayede her atamada heap alanında yeni bir alan açılmayacaktır.

using System;
using System.Text;
namespace StringConcentrationTip
{
	class Class1
	{
		[STAThread]
		static void Main(string[] args)
		{
			Console.WriteLine("1- Yöntem 1 ile 100.000 defa string birleştir.");
			Console.WriteLine("2- Yöntem 2 ile 100.000 defa string birleştir.");
			if (Console.ReadLine().ToString()=="1")
			{
				Console.WriteLine("--işlem yapılıyor--");
				Console.WriteLine("Başlangıç: " + DateTime.Now.ToString());
				string strA="";
				for (int intA=0;intA<100000;intA++)
					strA+="A";
				Console.WriteLine("Bitiş: " + DateTime.Now.ToString());
				Console.WriteLine("--işlem bitti--");
			}
			else
			{
				Console.WriteLine("--işlem yapılıyor--");
				Console.WriteLine("Başlangıç: " + DateTime.Now.ToString());
				StringBuilder stbA=new StringBuilder();
				for (int intA=0;intA<100000;intA++)
					stbA.Append("A");
				Console.WriteLine("Bitiş: " + DateTime.Now.ToString());
				Console.WriteLine("--işlem bitti--");
			}
		}
	}
}

Çok fark var değilmi?

(Bu arada heap alanında açılıp bırakılan kullanılmayan adreslere ne olacak diye soranlar için ; Garbage Collector belirsiz bir zamanda heap'i temizleyecektir.)

Süper... o zaman bütün string işlemlerimizde stringbuilder'ı kullanalım?
Evet ben de ilk denediğim zaman farkı görüp heryerde bu sınıfı kullanmaya karar vermiştim.Fakat yazılım bu , karşımıza çeşitli algoritmalar çıkabiliyor.
İyi de stringbuilder'ı övdün övdün şimdi neden böyle diyorsun diye söylendiğinizi duyabiliyorum :)

Aşağıdaki örneği inceleyelim...

Console.WriteLine(" + kullanılarak işlem yapılıyor");
Console.WriteLine("Başlangıç: " + DateTime.Now.ToString());
string strA="";
for (int intA=0;intA<10000000;intA++)
	strA="A"+"A"+"A"+"A"+"A"+"A"+"A"+"A"+"A"+"A";
Console.WriteLine("Bitiş: " + DateTime.Now.ToString());
Console.WriteLine("-------------------------------");

Console.WriteLine(" Concat kullanılarak işlem yapılıyor");
Console.WriteLine("Başlangıç: " + DateTime.Now.ToString());
strA="";
for (int intA=0;intA<10000000;intA++)
	strA =string.Concat("A"+"A"+"A"+"A"+"A"+"A"+"A"+"A"+"A"+"A");
Console.WriteLine("Bitiş: " + DateTime.Now.ToString());
Console.WriteLine("-------------------------------");

Console.WriteLine(" stringbuilder kullanılarak işlem yapılıyor");
Console.WriteLine("Başlangıç: " + DateTime.Now.ToString());
strA="";
for (int intA=0;intA<10000000;intA++)
{
	StringBuilder stbA=new StringBuilder();
	stbA.Append("A");
	stbA.Append("A");
	stbA.Append("A");
	stbA.Append("A");
	stbA.Append("A");
	stbA.Append("A");
	stbA.Append("A");
	stbA.Append("A");
	stbA.Append("A");
	stbA.Append("A");
}	
Console.WriteLine("Bitiş: " + DateTime.Now.ToString());
Console.WriteLine("-------------------------------");

Bu örnekte 3 yöntem kullanılarak string concentration işlemi yapılmaktadır.
1.yöntem standart olan + operatörü  ile 
2.yöntem string sınıfının concat metodunu kullanarak
3.yöntem ise stringbuilder sınıfını kullanarak.

Örneği çalıştırdığınızda ilk örneğin aksine 1.yöntem en hızlısı olacaktır.
Hele stringbuilder'ın zamanına baktığınızda diğer 2 yöntemden çok daha fazla zaman aldığını göreceksiniz.Dikkat ederseniz bu örnekte döngünün her safhasında string değişkenine yeni bir değer atanıyor,eski değeri korunmuyor.

Artık kurduğunuz algoritmaya göre hangi yöntemi kullanacağınıza karar verebilirsiniz.
Umarım yararlı olmuştur.Bir sonraki makalede görüşmek üzere.

Levent YILDIZ
Gulf Agency Company – TURKEY
IT Manager
MCP
msmoracle@hotmail.com
levent.yildiz@gacworld.com
http://www.gacworld.com

Örnek proje