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
| Interface | Mô tả | Ví dụ |
|---|---|---|
IEnumerable<T> | Chỉ hỗ trợ iteration | LINQ queries |
ICollection<T> | Thêm Count, Add, Remove, Clear | List<T>, HashSet<T> |
IList<T> | Thêm index-based access | List<T>, arrays |
IDictionary<TKey, TValue> | Key-value pairs | Dictionary<TKey, TValue> |
IReadOnlyCollection<T> | Chỉ đọc với Count | IEnumerable<T>.ToList() |
Span và Memory - High-Performance Memory Views
Lưu ý:
Span<T>vàMemory<T>không phải là collections theo nghĩa truyền thống. Chúng là cácref structcung 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ểm | Span<T> | Collections (List<T>, T[]) |
|---|---|---|
| Kiểu | ref struct (stack-only) | Reference type hoặc array |
| Allocation | Zero heap allocation | Heap allocation |
| Lưu trữ | Không thể là field của class | Có thể là field |
| Async | Không hỗ trợ (ref struct limitation) | Hỗ trợ đầy đủ |
| IEnumerable | Không implement | Có implement |
| Performance | Cao nhất | Thấp hơn do allocation |
| Use case | Parsing, slicing, buffer manipulation | General 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ống | Khuyến nghị |
|---|---|
| Parsing string/byte buffer | Span<T> |
| Slicing array mà không copy | Span<T> |
| High-performance buffer manipulation | Span<T> |
| Cần lưu trữ trong class | Memory<T> |
| Cần sử dụng với async/await | Memory<T> |
| General purpose data storage | List<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?
- Không implement
IEnumerable<T>- Không thể dùngforeachtrực tiếp (phải dùngforeach(ref var item in span)) - Không implement
ICollection<T>- Không cóAdd,Remove,Clear - 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 - Stack-only - Không thể boxing, không thể lưu trong heap
Tóm lại:
Span<T>vàMemory<T>là 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.