Это, кажется, еще один случай, когда использование XPath кажется медленным, но вместо XPath причина, вероятно, вызвана методом DOM nodelist.item(i)
Реализация по умолчанию NodeList
в Java имеет определенные особенности:
- оценивается лениво
- Список DOM активен
- Он реализован в виде связанного списка
- В списке есть некоторое кэширование
Когда вы смотрите на эти функции отдельно, вы можете задаться вопросом, почему объект-результат выражения XPath должен иметь такую функцию, но они имеют больше смысла, когда вы их объединяете.
1)
Ленивая оценка может размыть местоположение узкого места производительности. Из-за этого возврат NodeList кажется быстрым, но если задача состоит в том, чтобы всегда перебирать список, он более или менее просто откладывает снижение производительности. Ленивая оценка становится дорогостоящей, если оценка всего списка должна обрабатываться снова каждый раз, когда читается следующий элемент в списке.
2)
NodeList
«живой» список означает, что он обновляется и ссылается на узлы, которые в данный момент находятся в дереве документа, а не на узлы, которые были в дереве, когда список был изначально создан, или на клоны этих узлов. Это важная функция для начинающих DOM. Например, если вы выберете NodeList
элементов-братьев и попытаетесь добавить по одному новому элементу-брату к каждому узлу, шаг к item(i+1)
всегда достигнет последнего добавленного узла, и цикл никогда не завершится.
3)
Актуальный список также дает некоторое объяснение, почему он реализован как связанный список (или AFAIK фактическая реализация - это дважды связанный список). Эффект этого отчетливо виден в вашем тесте, где доступ к последним элементам всегда самый медленный, независимо от того, повторяете ли вы его вперед или назад.
4)
Из-за кэширования зацикливание одного списка без каких-либо изменений в дереве должно быть достаточно эффективным, если кэш остается чистым. В некоторых версиях Java были проблемы с этим кэшированием. Я не исследовал, какие все процедуры делают недействительным кэширование, но, вероятно, самые безопасные ставки - посоветовать сохранить одинаковое вычисленное выражение, не вносить изменений в дерево, выполнять циклический переход по одному списку за раз и всегда переходить к следующему или предыдущему элементу списка.
Реальные выигрыши в производительности, конечно, зависят от варианта использования. Вместо того, чтобы просто настраивать зацикливание списка, вы должны попытаться вообще избавиться от зацикливания живого списка - по крайней мере, для справки. Клонирование делает список не живым. Прямой доступ к узлам может быть достигнут путем копирования узлов в массив. Если структура подходит, вы также можете использовать другие методы DOM, такие как getNextSibling()
, которые говорят, что дают более эффективные результаты, чем зацикливание на NodeList.