• شوفنا في ال Cs String اننا لما بنيجي نعمل concatenate أو نزود أي حاجة في ال string هتعمل حرفيًا string جديدة كليًا ودا بيأثر على الأداء والميموري ودا لأن ال string عبارة عن Immutable
  • الحل كان إننا نستخدم class من نوع StringBuilder

StringBuilder

  1. Initialization: Create a StringBuilder instance.

    StringBuilder sb = new StringBuilder();
  2. Appending Strings: Use Append() to add strings.

    sb.Append("Hello");
    sb.Append(" ");
    sb.Append("World");
  3. Inserting and Modifying: Use Insert() for inserting and Replace() for replacing.

    sb.Insert(6, "beautiful "); // inde
    sb.Replace("World", "Universe");
  4. Removing: Remove characters with Remove().

    sb.Remove(6, 10); //start index, length
  5. Getting the Result: Convert to string using ToString().

    string result = sb.ToString();
  6. Clearing: Clear the StringBuilder.

    sb.Clear();

Example

using System;
using System.Text;
 
class Program
{
    static void Main()
    {
        StringBuilder sb = new StringBuilder();
        
        sb.Append("Hello");
        sb.Append(" ");
        sb.Append("World");
        sb.Append("!");
        
        Console.WriteLine(sb.ToString()); // Output: Hello World!
        
        sb.Insert(6, "beautiful ");
        sb.Replace("World", "Universe");
        Console.WriteLine(sb.ToString()); 
        // Output: Hello beautiful Universe
        
        sb.Remove(6, 10);
        Console.WriteLine(sb.ToString()); // Output: Hello Universe
        
        sb.Clear();
        Console.WriteLine(sb.ToString()); // Output: ""
    }
}

ايه اللي يفرقها عن ال String

عندنا اتنين Strategies:

Array Reallocation and Doubling

  • بكل بساطة إن لما ال Array بتتملى بيروح يعمل array جديدة
    • (بتبقا ضعف ال capacity بتاع القديمة) وشرحنا ليه في Amortized analysis
  • بينقل كل الداتا من القديمة للجديدة وبعدين يزود الداتا الجديدة
  • نفس فكرة ال Dynamic Arrays في أغلب لغات البرمجة

Linked List of Small Arrays

في الـ StringBuilder في النسخة الجديدة، غيروا بدل ما بنستخدم array واحدة بيبقا حجمها كبير بنعمل شوية arrays صغيرة ونربطهم سوا ب Linked list

  • أول ما ال Array تتملى، بتتعمل Array جديدة small وتتحط في آخر ال List
  • دا بيحللي مشكلة إني أحتاج مساحة كبيرة ورا بعض في الميموري عشان أخزن فيها Array واحدة فبيحسن ال memory

مش متأكد أوي من الموضوع دا

بس اللي قولته هنا دا نفس فكرة الـ Chunked List

Chunked List (Advanced - Not important)

اللي انت بتوصفه ده في الـC# بيكون عادةً إما Custom Implementation لـ”Chunked List” أو ممكن يكون حديث عن الـ”Unrolled Linked List” أو أي بنية بيانات بتستخدم فكرة تقسيم الداتا لـأجزاء صغيرة (chunks) عشان تتجنب مشاكل الـMemory Contiguous Allocation.

إيه المشكلة الأساسية في الـArray العادية؟

  • الـArray العادي في السي شارب بيحتاج Memory Contiguous Block (مساحة متجاورة في الذاكرة) عشان يخزن العناصر.
  • لو عايز تزيد حجم الـArray، لازم تعمل Resize (إنشاء array جديدة أكبر + نسخ العناصر القديمة)، دا مكلف في الـPerformance لو الداتا كبيرة.

الفكرة الجديدة اللي انت بتتكلم عليها:

  • بدل ما تستخدم array واحدة كبيرة، هتقسم الداتا لـGroup of Small Arrays (Chunks) وتربطهم سوا باستخدام Linked List.
  • كل ما الـChunk الحالي يتملى، هتعمل Chunk جديد وتضيفه في الآخر.
  • دا بيحل مشكلة الـContiguous Memory Allocation، وبيقلل تكلفة الـResize.

ده بيتطبق في إيه في السي شارب؟

1. لو انت عامله Implementation بنفسك:
  • هتستخدم LinkedList<T[]> أو List<T[]> عشان تخزن الـChunks.
  • مثال:
public class ChunkedList<T>
{
 private LinkedList<T[]> _chunks = new LinkedList<T[]>();
 private int _chunkSize = 1000; // حجم كل chunk
 private int _currentIndex = 0;
 
 public void Add(T item)
 {
	 if (_currentIndex == 0 || _currentIndex == _chunkSize)
	 {
		 _chunks.AddLast(new T[_chunkSize]);
		 _currentIndex = 0;
	 }
	 _chunks.Last.Value[_currentIndex] = item;
	 _currentIndex++;
 }
}
2. لو بتستخدم مكتبات جاهزة:
  • مفيش بنية بيانات جاهزة في الـStandard C# Library بتعمل بالضبط ده، لكن ممكن تلاقيها في مكتبات خارجية أو في سيستمات خاصة (مثلاً في الـGame Development).
3. لو بتتكلم على الـMemory Management بشكل عام:
  • الفكرة دي شبه الطريقة اللي الـGC (Garbage Collector) في الـC# بيدير بيها الـHeap، بيقسم الذاكرة لـأجزاء صغيرة عشان يتعامل معها بسهولة.

مقارنة بين الـChunked List والـArray العادي:

Array/List العاديChunked List
الـMemoryمحتاج مساحة متجاورة (Contiguous)الداتا متوزعة على أجزاء (Non-Contiguous)
الـResizeمكلف (نسخ كل العناصر)رخيص (إضافة chunk جديد)
الـRandom Accessسريع (O(1))أبطأ (O(n/chunkSize))
التعقيدجاهز في الـLanguageمحتاج Implementation منك

امتى تستخدم الفكرة دي؟

  • لو عندك داتا ضخمة جداً ومش عايز تضطر تنسخها كل مرة.
  • لو الـMemory Fragmentation عندك عالي (مشاكل في إيجاد مساحة متجاورة).
  • لو بتتعامل مع Real-Time Systems وعايز تقلل الـLatency.

مثال واقعي:

تخيل إنك بتعمل برنامج بيتعامل مع مليون عنصر:

  • لو استخدمت List<T> عادي، كل ما الـCapacity يخلص، هيتعمل Resize (مثلاً من 1M لـ2M عنصر) وبيحصل نسخ لكل المليون عنصر.
  • لو استخدمت Chunked List بكل chunk حجمه 1000 عنصر، كل ما الـChunk يتملى هتضيف واحد جديد من غير ما تنسخ أي حاجة.

هل ده موجود في الـC# بالفعل؟

  • لأ، ده بيكون Custom Implementation، لكن ممكن تشوف حاجات مشابهة في:
    • الـSystem.Collections.Immutable: فيها أنواع Lists وArrays مبنية على فكرة الـChunks.
    • الـ**Memory<T> وSpan<T>**: بتساعدك في التعامل مع الـMemory بشكل أكثر تحكمًا.