Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Collections

Arrays

// Single-dimensional array
int[] numbers = new int[5];
numbers[0] = 1;

int[] initialized = { 1, 2, 3, 4, 5 };

// Multi-dimensional array
int[,] matrix = new int[3, 3];
matrix[0, 0] = 1;

// Jagged array (array of arrays)
int[][] jagged = new int[3][];
jagged[0] = new int[] { 1, 2, 3 };

List

List<string> names = new List<string>();
names.Add("Alice");
names.Add("Bob");
names.Add("Charlie");

// Initialization
var numbers = new List<int> { 1, 2, 3, 4, 5 };

// Common operations
bool contains = names.Contains("Alice"); // true
int index = names.IndexOf("Bob"); // 1
names.Remove("Charlie");
names.Sort();

// Capacity management
names.Capacity = 100; // Pre-allocate
names.TrimExcess(); // Reduce capacity

Dictionary<TKey, TValue>

Dictionary<string, int> ages = new Dictionary<string, int>();
ages["Alice"] = 30;
ages["Bob"] = 25;

// Safe access
if (ages.TryGetValue("Alice", out int age))
{
    Console.WriteLine($"Alice is {age} years old");
}

// Initialization
var scores = new Dictionary<string, int>
{
    ["Alice"] = 95,
    ["Bob"] = 87,
    ["Charlie"] = 92
};

// Iteration
foreach (var kvp in ages)
{
    Console.WriteLine($"{kvp.Key}: {kvp.Value}");
}

HashSet

HashSet<int> uniqueNumbers = new HashSet<int>();
uniqueNumbers.Add(1);
uniqueNumbers.Add(2);
uniqueNumbers.Add(1); // Duplicate - ignored

// Set operations
var set1 = new HashSet<int> { 1, 2, 3 };
var set2 = new HashSet<int> { 3, 4, 5 };

set1.UnionWith(set2); // {1, 2, 3, 4, 5}
set1.IntersectWith(set2); // {3}
set1.ExceptWith(set2); // {1, 2}

Queue và Stack

// Queue (FIFO)
Queue<string> queue = new Queue<string>();
queue.Enqueue("First");
queue.Enqueue("Second");
string first = queue.Dequeue(); // "First"

// Stack (LIFO)
Stack<string> stack = new Stack<string>();
stack.Push("First");
stack.Push("Second");
string last = stack.Pop(); // "Second"

Collection Interfaces

InterfaceMô tảVí dụ
IEnumerable<T>Chỉ hỗ trợ iterationLINQ queries
ICollection<T>Thêm Count, Add, Remove, ClearList<T>, HashSet<T>
IList<T>Thêm index-based accessList<T>, arrays
IDictionary<TKey, TValue>Key-value pairsDictionary<TKey, TValue>
IReadOnlyCollection<T>Chỉ đọc với CountIEnumerable<T>.ToList()

Span và Memory - High-Performance Memory Views

Lưu ý: Span<T>Memory<T> không phải là collections theo nghĩa truyền thống. Chúng là các ref struct cung cấp view an toàn, zero-allocation trên bộ nhớ liên tục.

Span là gì?

Span<T> là một stack-only type (ref struct) cho phép truy cập an toàn vào một vùng bộ nhớ liên tục mà không cần allocation trên heap.

// Tạo Span từ array
int[] array = { 1, 2, 3, 4, 5 };
Span<int> span = array.AsSpan();

// Slice - tạo view mà không copy dữ liệu
Span<int> slice = span[1..3]; // { 2, 3 }

// Truy cập phần tử
span[0] = 10; // array[0] cũng thay đổi

// Tạo Span từ stack
Span<int> stackSpan = stackalloc int[3] { 1, 2, 3 };

// Tạo Span từ string (readonly)
ReadOnlySpan<char> text = "Hello World".AsSpan();

Span vs Collections

Đặc điểmSpan<T>Collections (List<T>, T[])
Kiểuref struct (stack-only)Reference type hoặc array
AllocationZero heap allocationHeap allocation
Lưu trữKhông thể là field của classCó thể là field
AsyncKhông hỗ trợ (ref struct limitation)Hỗ trợ đầy đủ
IEnumerableKhông implementCó implement
PerformanceCao nhấtThấp hơn do allocation
Use caseParsing, slicing, buffer manipulationGeneral purpose storage

Memory - Phiên bản linh hoạt hơn

Memory<T> không phải ref struct nên có thể lưu trữ trong class và sử dụng với async:

public class DataProcessor
{
    // Memory<T> có thể là field của class
    private Memory<byte> _buffer;
    
    public DataProcessor(Memory<byte> buffer)
    {
        _buffer = buffer;
    }
    
    // Có thể dùng với async
    public async Task ProcessAsync()
    {
        // Span không thể dùng trong async method
        Span<byte> span = _buffer.Span;
        // ... process
    }
}

// Sử dụng
var memory = new Memory<byte>(new byte[1024]);
var span = memory.Span; // Lấy Span từ Memory

Khi nào sử dụng Span và Memory?

Tình huốngKhuyến nghị
Parsing string/byte bufferSpan<T>
Slicing array mà không copySpan<T>
High-performance buffer manipulationSpan<T>
Cần lưu trữ trong classMemory<T>
Cần sử dụng với async/awaitMemory<T>
General purpose data storageList<T>, T[]

Ví dụ thực tế: Parsing với Span

// Không allocation khi parsing
int ParseNumbers(ReadOnlySpan<char> input)
{
    int sum = 0;
    while (!input.IsEmpty)
    {
        int commaIndex = input.IndexOf(',');
        if (commaIndex == -1)
        {
            sum += int.Parse(input);
            break;
        }
        
        sum += int.Parse(input[..commaIndex]);
        input = input[(commaIndex + 1)..];
    }
    return sum;
}

// Sử dụng
var csv = "1,2,3,4,5";
int total = ParseNumbers(csv.AsSpan()); // total = 15

Tại sao Span không phải là Collection?

  1. Không implement IEnumerable<T> - Không thể dùng foreach trực tiếp (phải dùng foreach(ref var item in span))
  2. Không implement ICollection<T> - Không có Add, Remove, Clear
  3. Là view, không phải storage - Span<T> không sở hữu dữ liệu, nó chỉ “nhìn” vào vùng bộ nhớ có sẵn
  4. Stack-only - Không thể boxing, không thể lưu trong heap

Tóm lại: Span<T>Memory<T>memory views, không phải collections. Chúng bổ sung cho collections bằng cách cung cấp hiệu suất cao cho các thao tác trên bộ nhớ liên tục.