РЕДАКТИРОВАТЬ Сводка и рекомендации
Использование конструкции for each cell in range
само по себе не медленно. медленный медленный - это повторный доступ к Excel в цикле (будь то чтение или запись значений ячеек, формат и т. Д., Вставка / удаление строк и т. Д.).
То, что слишком медленно, зависит от ваших потребностей.Подпрограмма, для запуска которой требуются минуты, может быть в порядке, она используется редко, но другая, для которой требуются 10 секунд, может быть слишком медленной при частой работе.
Итак, некоторые общие рекомендации:
- keepэто просто на первый взгляд.Если результат слишком медленный для ваших нужд, тогда оптимизируйте
- , сосредоточьтесь на оптимизации содержимого цикла
- , а не просто предполагайте, что цикл необходим.Иногда существуют альтернативы
- , если вам нужно использовать значения ячеек (много) внутри цикла, загрузить их в массив вариантов вне цикла.
- хороший способ избежать сложности со вставками -чтобы зациклить диапазон снизу вверх
(for index = max to min step -1
) - , если вы не можете сделать это, и ваш «вставить строку здесь и там» не слишком много, попробуйте перезагрузить массив после каждогоinsert
- Если вам нужен доступ к свойствам ячейки, отличным от
value
, вы застряли со ссылками на ячейки - Чтобы удалить несколько строк, рассмотрите возможность создания ссылки на диапазон для диапазона нескольких областей вцикл, затем удалите этот диапазон за один раз после цикла
например (не проверено!)
Dim rngToDelete as range
for each rw in rng.rows
if need to delete rw then
if rngToDelete is nothing then
set rngToDelete = rw
else
set rngToDelete = Union(rngToDelete, rw)
end if
endif
next
rngToDelete.EntireRow.Delete
Исходное сообщение
Общепринятое мнение гласит, что цикл по ячейкам равен плохо , а цикл по массиву вариантов равен хорошо .Я тоже был сторонником этого в течение некоторого времени.Ваш вопрос заставил меня задуматься, поэтому я провел несколько коротких тестов с неожиданными (для меня в любом случае) результатами:
набор тестовых данных: простой список в ячейках A1
.. A1000000
(то есть 1 000 000 строк)
Контрольный пример 1: цикл массива
Dim v As Variant
Dim n As Long
T1 = GetTickCount
Set r = Range("$A$1", Cells(Rows.Count, "A").End(xlUp)).Cells
v = r
For n = LBound(v, 1) To UBound(v, 1)
'i = i + 1
'i = r.Cells(n, 1).Value 'i + 1
Next
Debug.Print "Array Time = " & (GetTickCount - T1) / 1000#
Debug.Print "Array Count = " & Format(n, "#,###")
Результат:
Array Time = 0.249 sec
Array Count = 1,000,001
Контрольный пример 2: цикл диапазона
T1 = GetTickCount
Set r = Range("$A$1", Cells(Rows.Count, "A").End(xlUp)).Cells
For Each c In r
Next c
Debug.Print "Range Time = " & (GetTickCount - T1) / 1000#
Debug.Print "Range Count = " & Format(r.Cells.Count, "#,###")
Результат:
Range Time = 0.296 sec
Range Count = 1,000,000
Итак, зацикливание массива происходит на быстрее, но только на 19% - намного меньше, чем я ожидал.
Тест 3: зацикливание массива со ссылкой на ячейку
T1 = GetTickCount
Set r = Range("$A$1", Cells(Rows.Count, "A").End(xlUp)).Cells
v = r
For n = LBound(v, 1) To UBound(v, 1)
i = r.Cells(n, 1).Value
Next
Debug.Print "Array Time = " & (GetTickCount - T1) / 1000# & " sec"
Debug.Print "Array Count = " & Format(i, "#,###")
Результат:
Array Time = 5.897 sec
Array Count = 1,000,000
Контрольный пример 4: диапазон цикла с ссылкой на ячейку
T1 = GetTickCount
Set r = Range("$A$1", Cells(Rows.Count, "A").End(xlUp)).Cells
For Each c In r
i = c.Value
Next c
Debug.Print "Range Time = " & (GetTickCount - T1) / 1000# & " sec"
Debug.Print "Range Count = " & Format(r.Cells.Count, "#,###")
Результат:
Range Time = 2.356 sec
Range Count = 1,000,000
Таким образом, событие с одной простой ссылкой на ячейку, цикл на порядок медленнее, и, кроме того, цикл диапазона в два раза быстрее!
Итак, вывод , что важнее всего, это то, что вы делаете внутри цикла , и, если скорость действительно имеет значение, протестируйте все опции
FWIW, протестировано в Excel 2010, 32-битная, Win7, 64-битная, все тесты с
ScreenUpdating
выключен, Calulation
ручной, Events
отключен.