• بكل بساطة، الـ Generic List هي نوع “عام” تقدر تحط فيه أي نوع من البيانات، لكن بشرط إنك تحدد النوع ده من الأول.
  • يعني لو قولتله “حط أرقام” مش هيقبل غير أرقام، ولو قولتله “حط نصوص” مش هيقبل غير نصوص.
  • هي زي ال Array بس الفرق انها Dynamic size نفس فكرة ال Dynamic Arrays
  • هي عبارة عن Cs Generics عشان كدا بنستخدم معاها <new List <int
    • لما تيجي تتعامل مع ال generic type لازم تحدد ال generic parameter اللي بتبقا بينangle brackets <>
  • تقدر تعمل list من أي نوع زي ال char او string أو حتى أي non primitive types زي ال classes

Tip

  • لما تيجي تحاول تستخدم ال AddRange هيقولك انه بياخد IEnumerable
  • هي نوعها Cs Enumerable

Syntax

List<int> numbers = new List<int>();
List<int> numbers = new List<int>() {1, 2, 3, 4};
  • هنا، حددنا نوع البيانات اللي هيتخزن جوه الليست إنها أرقام (int).
  • ده بيريحك من عمليات التحويل اللي بتعملها في الـ ArrayList وبيخلي الكود أسرع.

الفرق بين Generic List و ArrayList

  • الـArrayList تقدر تحط فيها أي نوع من البيانات، بس لما تيجي تستخدم البيانات دي، لازم تعمل لها Unboxing وترجعها لنوعها الأصلي.
  • الـGeneric List بتطلب منك تحدد نوع البيانات من الأول، فمفيش حاجة اسمها Boxing و Unboxing، وبالتالي الكود أسرع وأكتر أمان.

Methods

الـ Generic List بتديك كل المميزات اللي كانت موجودة في الـ ArrayList وزيادة.

  • Add: Add an object to the list
  • AddRange: Add a list of objects that can be list or array
  • Remove: To remove one object from the list
  • RemoveAt: Remove the object at the given index
  • IndexOf: Returns the index of the given object.
  • Contains: If the list contains the given object or not
  • Count: Return the number of objects in the list
  • TrueForAll: If all members make condition true, it will return true. Like [[LINQ#all|All()]]

Example

using System.Collections.Generic;
 
#region Lists
// Make new list
var numbers = new List<int>() { 1, 2, 3, 4 };
 
// Add Items
numbers.Add(1);
numbers.AddRange(new int[3] {5, 6, 7});
 
// Iterate Items
foreach (var number in numbers)
    Console.WriteLine(number); // 1 2 3 4 1 5 6 7
 
// The First Index
Console.WriteLine("Index: " + numbers.IndexOf(1)); // Index: 0
// The Last Index
Console.WriteLine("Last Index: " + numbers.LastIndexOf(1)); // Last Index: 4
 
// Count == Length in simple array
Console.WriteLine("Count: " + numbers.Count); // Count: 8
 
// Remove
numbers.Remove(1); // It will remove first 1
foreach (var number in numbers)
    Console.WriteLine(number);
 
// Remove all 1s
// Error
foreach (var number in numbers)
{
    if (number == 1) numbers.Remove(number); //Error
}
// It will give us exception because we can't change list and iterate it at same time
// In C#, you are not allowed to modify a Collection inside a foreach
 
 
 
for (var i = 0; i < numbers.Count; i++)
    if (numbers[i] == 1) numbers.Remove(numbers[i]);
 
foreach (var number in numbers)
    Console.WriteLine(number); // 2 3 4 5 6 7
 
// Remove all items
numbers.Clear();
Console.WriteLine("Count: " + numbers.Count); // Count = 0
#endregion

Fail

  • منقدرش نستخدم ال foreach وجواها نمسح حاجة من نفس ال collection هيجيبلي exception اللي اسمه InvalidOperationException
  • في الحالة دي نقدر نستخدم ال for loop العادية

الـCapacity و Resize في Generic List:

شرحنا طريقة العمل دي قبل كدا في Dynamic Array

Capacity and Count

الـ Generic List بيكون عندها حاجة اسمها Capacity.

يعني دي السعة القصوى اللي ممكن الليست تشيلها قبل ما تعمل عملية توسيع (Resize). لما الليست تتملى على الآخر، بتكبر نفسها تلقائيًا عشان تشيل عناصر أكتر.

دول مصطلحين مهمين جدًا لازم نعرفهم:

  • Count: عدد العناصر الفعلية في القائمة.
  • Capacity: السعة القصوى التي يمكن للقائمة استيعابها قبل إعادة التخصيص.
List<int> list = new List<int>(100); 
// Capacity = 100, Count = 0

How it works

عند إضافة عناصر تتجاوز السعة الحالية:

  1. تضاعف السعة: (مثال: من 4 → 8 → 16 → …)
  2. تنشئ مصفوفة جديدة بحجم السعة الجديدة.
  3. نسخ العناصر من المصفوفة القديمة إلى الجديدة.

Methods in details

Add

الـ Add في الـ Generic List بيسمحلك تضيف عناصر جديدة.

الليست بتشتغل بكفاءة طالما لسا عندها مساحة فاضية، ولو المساحة خلصت، هي بتزود نفسها تلقائيًا.

public void Add(T item)
{
    if (_size < _items.Length)
    {
        // إضافة مباشرة إذا كان هناك مساحة
        _items[_size++] = item;
    }
    else
    {
        AddWithResize(item); // التوسع عند الامتلاء
    }
}
  • في الأول، بيشوف إذا كانت السعة لسا فيها مكان كافي للعنصر الجديد. لو لسا فيه مكان:

    • بيزود الـ size بواحد، ويحط العنصر في المكان الفاضي.
  • أما لو السعة اتملت، بيلجأ للـ AddWithResize عشان يعمل توسيع للذاكرة.

Add with Resize

الـ AddWithResize هو اللي بيقوم بعملية التوسيع لو الليست اتملت وما فيش مكان فاضي.

private void AddWithResize(T item)
{
    Debug.Assert(_size == _items.Length); 
    // يتأكد إن المساحة اتملت
 
    int size = _size;
 
    // ينادي على الميثود اللي بتعمل التوسيع
    Grow(_size + 1); // زيادة السعة
    _size = size + 1;
 
    _items[_size] = item; // إضافة العنصر الجديد
}
  • في الأول بيتأكد إن المساحة اتملت، وبعدين ينادي على Grow عشان تعمل التوسيع وتجهز مساحة جديدة للعناصر اللي جايه.

Grow

الميثود Grow هي اللي بتقوم بالتوسيع الفعلي للليست.

private void Grow(int requiredCapacity)
{
    int newCapacity = _items.Length == 0 ? 4 : _items.Length * 2; 
    // السعة الجديدة بتبقى ضعف السعة القديمة لو مش فاضية
    
    if (newCapacity > Array.MaxLength) newCapacity = Array.MaxLength; 
	// الحد الأقصى للنظام
 
    if (newCapacity < requiredCapacity) newCapacity = requiredCapacity;
	// إذا كانت السعة المطلوبة أكبر
    
    Capacity = newCapacity; // تعيين السعة الجديدة
}
  • أول حاجة بتعملها Grow إنها بتشوف لو الليست فاضية (السعة بصفر)، لو فعلاً فاضية، بتحط السعة الافتراضية اللي هي 4.

  • لو مش فاضية، بتضاعف السعة (بتضربها في 2). يعني كل ما الليست تتملى، السعة بتزيد للضعف.

  • لو السعة الجديدة أكبر من الحد الأقصى (Array.MaxLength)، بيروح يحدد السعة للحد الأقصى المسموح بيه.

Make it Manually

list.Capacity = 1000; 
// تجنب التوسعات المتكررة إذا كنت تعرف عدد العناصر مسبقًا
 
list.TrimExcess(); 
// يقلل السعة لتتطابق مع العدد الفعلي للعناصر (إن كانت < 90% من السعة)

Efficiency

  • مقارنة الأداء:
العمليةالتعقيد الزمني
Add (مع مساحة)O(1)
Add (بدون مساحة)O(n)
RemoveO(n)
  • نصائح للأداء الأمثل:
    • تجنب الإضافات المتكررة بدون تحديد سعة مبدئية
    • استخدم TrimExcess() بعد الانتهاء من التعديلات إذا كانت الذاكرة مهمة.
    • لا تستخدم Capacity مع الأحجام الكبيرة جدًا (قد يسبب إهدارًا للذاكرة).

Example

List<int> numbers = new List<int>(); 
// Capacity = 0 (حتى أول إضافة)
numbers.Add(1); 
// Capacity يصبح 4
numbers.AddRange(new[] {2, 3, 4, 5}); 
// Capacity يزيد إلى 8 بعد إضافة العنصر الخامس
Console.WriteLine($"Capacity: {numbers.Capacity}, Count: {numbers.Count}"); // Capacity: 8, Count: 5

Performance

أسرعهم الـ Array وبعدين الـ List وبعدين الـ Dictionary

أنت مش هتحس بيه غير في البرامج الكبيرة جدًا أنت مش هتوصلها