Ответ
Сложность операций зависит от внутренней реализации структуры данных. Вот ключевые коллекции .NET:
Списки и массивы
List<T>,T[](массив)- Доступ по индексу: O(1)
- Поиск элемента (
Contains,IndexOf): O(n) - Добавление в конец (
Add): O(1) (амортизированное, за счет увеличения емкости) - Вставка/удаление в середину (
Insert,RemoveAt): O(n) (требует сдвига элементов)
Хеш-таблицы (основанные на Dictionary<TKey, TValue>)
Dictionary<TKey, TValue>,HashSet<T>- Вставка, удаление, поиск по ключу: O(1) в среднем случае.
- В худшем случае (все ключи имеют один хеш) — O(n). Качественная реализация
GetHashCode()критически важна. - Перебор всех элементов: O(n).
Сбалансированные деревья (основанные на красно-черных деревьях)
SortedDictionary<TKey, TValue>,SortedSet<T>- Вставка, удаление, поиск: O(log n).
- Элементы хранятся в отсортированном порядке (по
IComparer<T>).
Двусвязный список
LinkedList<T>- Вставка/удаление в начало/конец или рядом с известным узлом: O(1).
- Поиск элемента по значению или по индексу: O(n).
Практический пример выбора коллекции:
// Частый поиск по ID -> Используем Dictionary
Dictionary<int, User> userCache = new();
User u = userCache[123]; // O(1)
// Частая вставка/удаление в начале/конце -> LinkedList
LinkedList<LogEntry> recentLogs = new();
recentLogs.AddFirst(newLog); // O(1)
// Нужен отсортированный набор уникальных элементов -> SortedSet
SortedSet<int> uniqueScores = new() { 95, 80, 95, 70 }; // {70, 80, 95}
bool added = uniqueScores.Add(85); // O(log n) Ответ 18+ 🔞
Да ты посмотри, какие коллекции в .NET есть, блядь! Сложность операций у них — это вообще отдельная песня, зависит от того, как они внутри устроены. Сейчас разжую, чтобы понятно было, а то некоторые думают, что всё работает по волшебству.
Списки и массивы — List<T> и T[]
Ну, это как твоя тёща на диване — доступ к любому месту по индексу моментальный, O(1), подошёл и взял. А вот найти что-то конкретное (Contains, IndexOf) — это уже O(n), потому что придётся перебрать всё, как будто ищешь пульт от телика. Добавить в конец (Add) — обычно быстро, O(1), но иногда он внутри переезжает в новую квартиру побольше, и тогда немного тормозит. А вот вставить или удалить что-то из середины — это пиздец, O(n), потому что придётся всю очередь за тобой сдвигать, как в поликлинике.
Хеш-таблицы — Dictionary<TKey, TValue> и HashSet<T>
Вот это, блядь, магия! Вставка, удаление, поиск по ключу — в среднем O(1), то есть почти мгновенно. Как будто у тебя всё разложено по полочкам с волшебными метками. Но есть нюанс, ёпта! Если все ключи вдруг окажутся братьями-близнецами и дадут одинаковый хеш — будет полный пиздец, O(n), и превратится всё в тот же долгий список. Поэтому GetHashCode() писать надо с умом, а не как попало. Перебрать всё подряд — O(n), куда ж деваться.
Сбалансированные деревья — SortedDictionary<TKey, TValue> и SortedSet<T>
Эти товарищи — перфекционисты. Вставка, удаление, поиск — O(log n), то есть работают чётко и предсказуемо, но не мгновенно. Зато они держат всё в отсортированном порядке, прямо как библиотекарь-педант. Хочешь порядок — плати временем.
Двусвязный список — LinkedList<T>
Специалист узкого профиля, блядь. Вставить или удалить в начале, в конце или рядом с известным узлом — O(1), раз плюнуть. Но попробуй найти что-то по значению или добраться до элемента по индексу — и всё, O(n), будешь шагать от начала до конца, как по коридору общежития.
А теперь пример из жизни, чтобы вообще всё встало на свои места:
// Если тебе надо часто искать юзера по айдишнику — бери Dictionary, не мучайся.
Dictionary<int, User> userCache = new();
User u = userCache[123]; // O(1) — взял и готово, красота!
// Если постоянно пихаешь новые записи в начало лога — LinkedList твой друг.
LinkedList<LogEntry> recentLogs = new();
recentLogs.AddFirst(newLog); // O(1) — прилепил и забыл.
// А если нужен отсортированный набор уникальных чисел — вперёд, в SortedSet.
SortedSet<int> uniqueScores = new() { 95, 80, 95, 70 }; // Получится {70, 80, 95}
bool added = uniqueScores.Add(85); // O(log n) — вставит куда надо, на своё законное место.
Короче, выбирай инструмент по задаче, а не потому что он первый в списке всплыл. Иначе потом будешь сидеть и думать, почему всё тормозит, как черепаха в патоках.