Наївні реалізації дійсно викрили б цю проблему - коли ви створюєте нову структуру даних замість того, щоб оновлювати існуючу на місці, ви повинні мати деяку накладну витрату.
У різних мовах є різні способи впоратися з цим, і є кілька хитрощів, якими користується більшість з них.
Одна з стратегій - вивезення сміття . У момент, коли нова структура була створена, або незабаром після цього, посилання на стару структуру виходять за межі сфери, і збирач сміття збирає її миттєво або досить швидко, залежно від алгоритму GC. Це означає, що хоча все ще є накладні витрати, вони є лише тимчасовими, і не збільшуватимуться лінійно з кількістю даних.
Ще одна - вибір різних типів даних. Там, де масиви є структурою даних списку переходів на імперативних мовах (як правило, загорнуті у якийсь контейнер динамічного перерозподілу, наприклад std::vector
у C ++), функціональні мови часто віддають перевагу зв'язаним спискам. За допомогою пов'язаного списку операція "Prepend" ("мінуси") може повторно використовувати існуючий список як хвіст нового списку, тому все, що дійсно виділяється, - це нова глава списку. Подібні стратегії існують і для інших типів структур даних - наборів, дерев, ви їх називаєте.
А далі ледача оцінка, а-ля Хаскелл. Ідея полягає в тому, що створені вами структури даних створюються не повністю; натомість вони зберігаються як "громи" (ви можете думати про це як рецепти для побудови значення, коли це потрібно). Тільки тоді, коли потрібне значення, грона розгортається до фактичного значення. Це означає, що розподіл пам’яті може бути відкладений до тих пір, поки не буде необхідна оцінка, і в цей момент в одному розподілі пам’яті можуть бути об’єднані декілька гронів.