- شوفنا في ال Cs String اننا لما بنيجي نعمل concatenate أو نزود أي حاجة في ال string هتعمل حرفيًا string جديدة كليًا ودا بيأثر على الأداء والميموري ودا لأن ال string عبارة عن Immutable
- الحل كان إننا نستخدم class من نوع
StringBuilder
StringBuilder
-
Initialization: Create a
StringBuilder
instance.StringBuilder sb = new StringBuilder();
-
Appending Strings: Use
Append()
to add strings.sb.Append("Hello"); sb.Append(" "); sb.Append("World");
-
Inserting and Modifying: Use
Insert()
for inserting andReplace()
for replacing.sb.Insert(6, "beautiful "); // inde sb.Replace("World", "Universe");
-
Removing: Remove characters with
Remove()
.sb.Remove(6, 10); //start index, length
-
Getting the Result: Convert to string using
ToString()
.string result = sb.ToString();
-
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 بشكل أكثر تحكمًا.