Підрозділ 10.Q
Питання для самоконтролю
Питання для самоконтролю — Розділ 10. Колекції 10.1. Список List<T 1. Що таке List<T з точки зору внутрішньої реалізації? Поясніть різницю між Count і Capacity . Що відбувається, коли Count досягає Capacity ? 2
Питання для самоконтролю — Розділ 10. Колекції
10.1. Список List
Що таке
List<T>з точки зору внутрішньої реалізації? Поясніть різницю міжCountіCapacity. Що відбувається, колиCountдосягаєCapacity?Що виведе наступний код? Поясніть складність кожної операції.
var list = new List<string> { "А", "Б", "В" }; list.Insert(0, "Початок"); list.Remove("Б"); list.Add("Г"); Console.WriteLine(list.Count); Console.WriteLine(list[0]);Поясніть стратегію подвоєння ємності в
List<T>. Чому складністьAddє амортизованою O(1), а не завжди O(1)?Порівняйте
List<T>іT[](масив) за такими критеріями: зміна розміру, індексований доступ, частота вставки в середину. Коли варто обирати масив?Чим відрізняються методи
Remove(item)іRemoveAt(i)? Яка складність кожного і чому?Напишіть код, який з колекції
List<Patient>(деPatientмає поляNameтаIsActive) видаляє всіх неактивних пацієнтів одним викликом методу. Який метод для цього потрібен?Поясніть, що таке
Predicate<T>і як він використовується в методахFind,FindAll,Exists,RemoveAll. Наведіть приклад лямбда-вираза для кожного.Якщо відомо наперед, що список міститиме близько 10 000 елементів, як ефективно ініціалізувати
List<T>, щоб уникнути зайвих перевиділень пам'яті? Запишіть відповідний код.
10.2. Двозв'язаний список LinkedList
Що таке двозв'язаний список і як він відрізняється від
List<T>на рівні внутрішньої структури? Що зберігаєLinkedListNode<T>крім самого значення?Поясніть, чому вставка та видалення у
LinkedList<T>на відомій позиції є O(1), тоді як пошук конкретного елемента — O(n).Що виведе наступний код?
var list = new LinkedList<int>(new[] { 1, 2, 3, 4 }); var node = list.Find(2); list.AddAfter(node, 99); list.RemoveFirst(); Console.WriteLine(list.First.Value); Console.WriteLine(list.Count);Поясніть різницю між
Remove(T value)іRemove(LinkedListNode<T> node)уLinkedList<T>. Яка складність кожного варіанту і чому?Для якого сценарію
LinkedList<T>є кращим вибором ніжList<T>? Наведіть конкретний приклад задачі (не з лекції), де це дає реальну перевагу у складності.Реалізуйте функцію обходу
LinkedList<string>у зворотному порядку (відLastдоFirst) через циклwhile. Яка властивість вузла для цього потрібна?Порівняйте
LinkedList<T>іList<T>за витратами пам'яті. ЧомуLinkedList<T>використовує більше пам'яті на один елемент?Ситуація: вам потрібна черга з пріоритетами, де ургентні пацієнти вставляються на початок, а планові — в кінець. Яку колекцію обрати і чому?
10.3. Черга Queue
Що означає принцип FIFO? Поясніть його на прикладі черги пацієнтів у реєстратурі. Які два методи є ключовими для
Queue<T>?Яка внутрішня структура
Queue<T>і чому видалення з початку (Dequeue) є O(1), на відміну відList<T>.RemoveAt(0)— O(n)?Що виведе наступний код? Що станеться при останньому
Dequeue?var q = new Queue<string>(); q.Enqueue("Перший"); q.Enqueue("Другий"); q.Enqueue("Третій"); Console.WriteLine(q.Dequeue()); Console.WriteLine(q.Peek()); Console.WriteLine(q.Count); q.Dequeue(); q.Dequeue(); q.Dequeue(); // черга порожня!Чим
TryDequeueвідрізняється відDequeue? Коли слід використовуватиTry-варіант?Поясніть різницю між
Peek()іDequeue(). Для чого кориснийPeek()у реальному коді?Напишіть клас
AppointmentQueue, який обгортаєQueue<Patient>і має методи:Enqueue(Patient),ServeNext()(обслуговує і повертає наступного пацієнта абоnull, якщо черга порожня),Count.Реалізація черги через
List<T>з видаленням черезRemoveAt(0)— чому це антипатерн при великій кількості елементів? Оцініть складність для N операцій.У яких ситуаціях
Queue<T>є неправильним вибором? Наведіть конкретні сценарії, де краще підійдутьStack<T>абоList<T>.
10.4. Stack
Що означає принцип LIFO? Наведіть два практичних приклади з реального програмування (не з лекції), де LIFO є природньою моделлю.
Що виведе наступний код? Поясніть порядок виведення.
var stack = new Stack<string>(); stack.Push("А"); stack.Push("Б"); stack.Push("В"); foreach (var item in stack) Console.Write(item + " "); Console.WriteLine(); Console.WriteLine(stack.Pop()); Console.WriteLine(stack.Peek());Stack<T>ініціалізується зList<string> { "А", "Б", "В" }. Який елемент стане вершиною і чому? Перевірте свою відповідь логікою.Поясніть внутрішню структуру
Stack<T>: чомуPushіPopє O(1), і в якому випадку виникає O(n)?Реалізуйте функцію
bool IsBalanced(string expression), яка перевіряє правильність розстановки дужок()[]{}черезStack<char>.Порівняйте
Stack<T>іQueue<T>за такими критеріями: принцип обробки, типовий сценарій, методи додавання/вилучення, поведінкаforeach.Чому
ToArray()уStack<T>повертає елементи у порядку LIFO (вершина першою)? Як це може ввести в оману програміста?Спроєктуйте клас
HistoryNavigator<T>, який черезStack<T>реалізує функціональність "назад/вперед": методиNavigate(page),GoBack(),GoForward(). Які стеки потрібні?
10.5. Словник Dictionary
Що таке хеш-таблиця і як вона забезпечує O(1) пошук за ключем? Поясніть поняття бакету і колізії.
Що виведе наступний код? Знайдіть потенційну проблему.
var dict = new Dictionary<string, int> { ["A"] = 1, ["B"] = 2 }; dict["C"] = 3; dict["A"] = 10; Console.WriteLine(dict["A"]); Console.WriteLine(dict["D"]); // ?Поясніть різницю між
dict["key"]іdict.TryGetValue("key", out var value). Коли виникне виняток при першому варіанті?Чим
Add(key, value)відрізняється відdict[key] = valueпри вже існуючому ключі? Що відбудеться в кожному випадку?Напишіть метод
Dictionary<string, int> CountDiagnoses(List<Patient> patients), який підраховує, скільки разів зустрічається кожен діагноз. ВикористайтеTryGetValueабоContainsKey.Поясніть, чому метод
ContainsValueє O(n), тоді якContainsKey— O(1). Як це пов'язано з внутрішньою структурою словника?Що таке
HashSet<T>і чим він відрізняється відDictionary<K, V>? Наведіть три сценарії, деHashSet<T>є більш підходящим вибором, ніжList<T>.Для правильної роботи
Dictionary<K, V>тип ключа K повинен коректно реалізовувати два методи. Які саме і чому? Що відбудеться, якщо власний клас використовується як ключ без перевизначення цих методів?
10.6. Клас ObservableCollection
Яку ключову проблему вирішує
ObservableCollection<T>, яку не вирішуєList<T>? В яких фреймворках і технологіях вона є обов'язковою?Що таке подія
CollectionChangedі який об'єкт вона передає підписникам? Перелічіть типи дій (Action), про які сигналізує ця подія.Що виведе наступний код? У якому порядку з'являться повідомлення?
var col = new ObservableCollection<string>(); col.CollectionChanged += (s, e) => Console.WriteLine($"{e.Action}: {e.NewItems?[0] ?? e.OldItems?[0]}"); col.Add("Петренко"); col.Add("Коваль"); col.Remove("Петренко"); col[0] = "Мельник";Чим метод
Move(oldIndex, newIndex)відрізняється відRemoveAt+Insert? Чому він є унікальним дляObservableCollection<T>і чому він важливий для UI?Порівняйте
ObservableCollection<T>іList<T>за такими критеріями: сповіщення про зміни, методи, застосування, продуктивність при пакетних операціях.Поясніть антипатерн: чому не слід додавати 10 000 елементів до
ObservableCollection<T>по одному в циклі? Як правильно вирішити цю задачу?Напишіть клас
WardMonitor, який підписується наCollectionChangedі веде рахунок поточної кількості пацієнтів (враховуючи Add, Remove, Reset).В якому просторі імен знаходиться
ObservableCollection<T>? Чим він відрізняється відSystem.Collections.Generic? Що потрібно додати уusing?
10.7. Інтерфейси IEnumerable та IEnumerator
Що таке
IEnumerable<T>іIEnumerator<T>? Яку роль кожен з них грає в роботіforeach?Запишіть, у що компілятор розгортає наступний
foreach:foreach (var p in ward) Console.WriteLine(p);Перелічіть методи та властивості, що викликаються.
Що таке відкладене виконання (deferred execution) в контексті
IEnumerable<T>? Чому наступний код виводить[Generator]після рядка "Отримали IEnumerable"?IEnumerable<int> Gen() { Console.WriteLine("[Generator]"); yield return 1; } var seq = Gen(); Console.WriteLine("Отримали IEnumerable"); foreach (var x in seq) Console.WriteLine(x);Поясніть, що таке «подвійна пастка» відкладеного виконання. Коли виклик
foreachдвічі по одномуIEnumerable<T>може призвести до подвійного запиту до бази даних?Реалізуйте клас
EvenNumbers : IEnumerable<int>, який перебирає парні числа від 0 до заданогоmax. Використайтеyield returnвGetEnumerator.Що містить інтерфейс
IEnumerator<T>: методи і властивості? Чомуforeachзагортає виклики вtry/finallyзDispose()?Порівняйте оголошення параметра методу як
IEnumerable<T>таList<T>. Який варіант є більш гнучким і чому? Коли все ж потрібен конкретний тип?Знайдіть і поясніть помилку у наступному коді:
IEnumerable<string> GetFromDb() { /* дорогий запит */ yield return "A"; yield return "B"; } var results = GetFromDb(); int count = results.Count(); // System.Linq foreach (var r in results) Console.WriteLine(r);
10.8. Ітератори та yield return
Що таке ітераторний метод і які умови робить звичайний метод ітераторним? Що компілятор генерує замість методу з
yield return?Поясніть різницю між
yield returnіyield break. Коли потрібенyield break?Що виведе наступний код? Поясніть порядок виведення крок за кроком.
IEnumerable<int> Numbers() { Console.WriteLine("Старт"); yield return 1; Console.WriteLine("Після 1"); yield return 2; Console.WriteLine("Після 2"); } foreach (var n in Numbers()) Console.WriteLine($"Отримано: {n}");Поясніть, як компілятор перетворює ітераторний метод на клас-машину стану. Де зберігаються локальні змінні між двома викликами
MoveNext()?Напишіть ітераторний метод
IEnumerable<T> TakeWhile<T>(IEnumerable<T> source, Func<T, bool> predicate), який повертає елементи доки виконується умова, а потім зупиняється черезyield break.Порівняйте
yield returnз явним написанням класуIEnumerator<T>за обсягом коду, читабельністю та можливостями. В яких 5% випадків явнийIEnumerator<T>все ж кращий?Спроєктуйте ітераторний метод
IEnumerable<Patient> GetCritical(IEnumerable<Patient> all, int minSeverity), який лінивим чином фільтрує пацієнтів. Чому це краще заFindAllу контексті великих послідовностей?Що відбудеться, якщо викликати
foreachдвічі по одному і тому ж ітераторному методу? Чи виконається тіло методу двічі? Як це пов'язано зGetEnumerator()?