Предшествующий код оставляет возможности для внесения дальнейших улучшений
Предыдущий подход все еще демонстрирует значительную напрасную трату ресурсов, поскольку мы постоянно размещаем и освобождаем строки. Если мы собираемся создать код, обеспечивающий высокую производительность, то от создания лишних строковых объектов следует избавиться.
Избегайте размещения в памяти лишних объектов
Обратите внимание на то, что в приведенном выше коде мы часто вычисляем множество величин, которые нам не нужны. В частности, мы генерируем в каждой итерации цикла, по крайней мере, три новых строки: одну для первого сегмента, одну для среднего сегмента и одну для конечного сегмента исходной строки. Например,
строка big_blue_boat разбивается на три различных строки: big, blue и boat. Для создания этих строк требуется определенное время, а, кроме того, впоследствии мы должны освободить занимаемую ими память.
В алгоритме выполняется проверка только среднего сегмента, в ходе которой устанавливается, совпадает ли он с некоторым конкретным значением (blue). Если совпадения нет. дальнейшая обработка не требуется. Это означает, что большую часть времени мы напрасно распределяем память для строк, предназначенных для размещения среднего и конечного сегментов, даже если они используются алгоритмом всего лишь для того, чтобы вновь собрать целую строку из отдельных кусочков. Что если вместо создания целых строк из кусочков старой строки в каждой итерации цикла мы будем просто сохранять символьные индексные значения, которые указывают нам, где находятся символы подчеркивания (_) в строке? Мы можем сохранять эти данные в виде целых чисел, накладные расходы для которых должны быть значительно меньше, чем при размещении новых строк. Если мы поступим именно таким образом, то сможем использовать исходные строковые и индексные значения для сравнения строк, начиная с первого символа подчеркивания и доходя до второго символа подчеркивания (например, blue_). Лишь в тех случаях, когда обнаруживается совпадение, нам потребуется создавать дополнительную строку для замены среднего сегмента. В большинстве случаев ситуация для нас намного улучшится и нам не надо будет распределять память для каких-либо объектов или строк. Лишь в тех случаях, когда обнаруживается совпадение средних сегментов, нам потребуется выполнять дополнительные строковые операции, но в любом случае нам придется выполнить не больше операций, чем выполнялось ранее. Как бы то ни было, хуже не будет.
Листинг 8.7. Тестовый пример, демонстрирующий значительное уменьшение объема памяти, распределяемой для объектов (типичный образец существенной алгоритмической оптимизации первоначального варианта реализации интересующей нас функции)
Примечание. В этом примере используется класс PerformanceSampling, определенный ранее в данной книге.