Введение

В компьютерном программировании ленивая инициализация – это стратегия отсрочки создания объекта, вычисления значения или выполнения другого ресурсоемкого процесса до момента его первого использования. Это разновидность ленивых вычислений, относящаяся конкретно к созданию экземпляров объектов или других ресурсов. Обычно это достигается путем добавления проверки в метод доступа (или геттер свойства), чтобы определить, был ли уже инициализирован приватный член, выполняющий роль кэша. Если да, то он возвращается немедленно. Если нет, создается новый экземпляр, помещается в переменную-член и возвращается вызывающей стороне непосредственно перед первым использованием. Если объекты имеют свойства, которые используются редко, это может повысить скорость запуска. Средняя производительность программы может незначительно снизиться с точки зрения использования памяти (для переменных состояния) и тактов процессора (на их проверку), но затраты на создание объектов распределяются во времени ("амортизируются"), а не концентрируются на этапе запуска системы, что позволяет значительно улучшить медианное время отклика. В многопоточном коде доступ к лениво инициализированным объектам/состояниям должен быть синхронизирован для предотвращения гонок данных.

Теоретическая информатика

В области теоретической информатики, ленивая инициализация (также называемая ленивым массивом) — это техника проектирования структур данных, которые могут работать с памятью, не требующей предварительной инициализации. В частности, предположим, что у нас есть доступ к таблице T, состоящей из n неинициализированных ячеек памяти (пронумерованных от 1 до n), и мы хотим присвоить значения m ячейкам этого массива, например, присвоить T[ki] := vi для пар (k1, v1), …, (km, vm), где все ki различны. Техника ленивой инициализации позволяет сделать это всего за O(m) операций, вместо O(m+n) операций, необходимых для предварительной инициализации всех ячеек массива. Суть техники заключается в выделении таблицы V, хранящей пары (ki, vi) в произвольном порядке, и записи в каждую ячейку T[ki] позиции в V, где хранится ключ ki, оставляя остальные ячейки T неинициализированными. Это позволяет обрабатывать запросы следующим образом: при обращении к ячейке T[k] для некоторого k, мы проверяем, находится ли k в диапазоне {1, …, m}: если нет, то T[k] не инициализирована. В противном случае мы проверяем V[T[k]] и убеждаемся, что первый элемент этой пары равен k. Если это не так, то T[k] не инициализирована (и случайно оказалась в диапазоне {1, …, m}). В противном случае мы знаем, что T[k] действительно является одной из инициализированных ячеек, а соответствующее значение — второй элемент пары.