Я думаю, что магия вызвана сложностью - каждая клетка несет с собой много "багажа"
Сотни настроек для своего окружения, и большинство из них касается форматирования ячеек
Height
, Width
, RowHeight
, ColumnWidth
, Column
, Row
, Font
, IndentLevel
и т. Д.
Чтобы увидеть все свойства, обратите внимание на окно просмотра для Sheet1.Range("A1")
(свойства с +
рядом с ними являются сложными объектами со своим набором свойств)
![RangeProperties](https://i.stack.imgur.com/dOJOM.png)
Основная причина оптимизации с использованием массивов состоит в том, чтобы избежать всего форматирования
Каждая ячейка «знает» обо всех настройках независимо от того, изменены они или нет, и несет весь этот «вес». Большинство пользователей в большинстве случаев заботятся только о значении в ячейке и никогда не касаются форматирования. В редких случаях вы можете застревать, работая напрямую с объектом диапазона, если вам нужно изменить .Borders
, .Interior.Color
, .Font
и т. Д. Каждой отдельной ячейки, и даже в этом случае существуют способы группировки ячеек с одинаковым форматированием и изменения атрибуты всей группы одновременно
.
Продолжить аналогию с багажом (и это немного растягивает): в аэропорту, если мне нужно пополнить ручку для пассажира "Джон Доу" из его багажа уже в самолете, в подсобном помещении в в аэропорту я смогу это сделать (у меня есть вся необходимая информация), но мне понадобится время, чтобы идти туда-сюда с багажом. И для одного пассажира это может быть сделано за разумное количество времени, но сколько времени потребуется, чтобы пополнить 20 тыс. Ручек, или 100 тыс., Или миллион? (ОДИН-ПО-ОДИН)
Я рассматриваю взаимодействие Range
<->
VBA
одинаково: работать с отдельными ячейками по одной - это все равно, что перевозить каждый отдельный багаж на миллион пассажиров в подсобное помещение в задней части аэропорта. , Вот что делает это утверждение:
Sheet1.Range("A1:A1048576").Value = Sheet2.Range("A1:A1048576").Value
в отличие от извлечения всех ручек сразу из всех чемоданов, их повторного наполнения и складывания всех обратно
.
Копирование объекта диапазона в массив изолирует одно из свойств для каждой ячейки - его Value
(«ручка») от всех остальных настроек (Excel чрезвычайно эффективен в этом отношении) , Теперь у нас есть массив только значений, и никаких других настроек форматирования. Измените каждое значение отдельно в памяти, затем поместите их все обратно в объект диапазона:
Dim arr as Variant
arr = Sheet2.Range("A1:A1048576") 'Get all values from Sheet2 into Sheet1
Sheet1.Range("A1:A1048576") = arr
.
Здесь также отличаются параметры копирования / вставки:
Sheet2.Range("A1:A1048576").Copy
Sheet1.Range("A1:A1048576").PasteSpecial xlPasteAll
.
Timers for Rows: 1,048,573
xlPasteAll - Time: 0.629 sec; (all values + all formatting)
xlPasteAllExceptBorders - Time: 0.791 sec
xlPasteAllMergingConditionalFormats - Time: 0.782 sec; (no merged cells)
xlPasteAllUsingSourceTheme - Time: 0.791 sec
xlPasteColumnWidths - Time: 0.004 sec
xlPasteComments - Time: 0.000 sec; (comments test is too slow)
xlPasteFormats - Time: 0.497 sec; (format only, no values, no brdrs)
xlPasteFormulas - Time: 0.718 sec
xlPasteFormulasAndNumberFormats - Time: 0.775 sec
xlPasteValidation - Time: 0.000 sec
xlPasteValues - Time: 0.770 sec; (conversion from formula to val)
xlPasteValuesAndNumberFormats - Time: 0.634 sec
.
Другим аспектом, помимо массивов, являются типы индексов для структур данных
Для большинства ситуаций допустимы массивы, но когда требуется лучшая производительность, существуют объекты Dictionary
и Collection
Одна неэффективность массива заключается в том, что для поиска элементов нам нужно перебирать каждый из них
Более удобным вариантом может быть доступ к определенным элементам, намного быстрее
Dim d As Object 'Not indexed (similar to linked lists)
Set d = CreateObject("Scripting.Dictionary") 'Native to VB Script, not VBA
d.Add Key:="John Doe", Item:="31" 'John Doe - 31 (age); index is based on Key
d.Add Key:="Jane Doe", Item:="33"
Debug.Print d("Jane Doe") 'Prints 33
В словарях также есть очень полезный и быстрый метод проверки элементов d.Exists("John Doe")
, который возвращает True
или False
без ошибок (но в коллекциях нет). С массивом вам придется перебрать потенциально все элементы, чтобы узнать
Я думаю, что один из самых быстрых способов извлечь уникальные значения для больших столбцов - это объединить массивы и словари
Public Sub ShowUniques()
With Sheet1.UsedRange
GetUniques .Columns("A"), .Columns("B")
End With
End Sub
Public Sub GetUniques(ByRef dupesCol As Range, uniquesCol As Range)
Dim arr As Variant, d As Dictionary, i As Long, itm As Variant
arr = dupesCol
Set d = CreateObject("Scripting.Dictionary")
For i = 1 To UBound(arr)
d(arr(i, 1)) = 0 'Shortcut to add new items to dictionary, ignoring dupes
Next
uniquesCol.Resize(d.Count) = Application.Transpose(d.Keys)
'Or - Place d itms in new array (resized accordingly), and place array back on Range
' ReDim arr(1 To d.Count, 1 To 1)
' i = 1
' For Each itm In d
' arr(i, 1) = itm
' i = i + 1
' Next
' uniquesCol.Resize(d.Count) = arr
End Sub
From: Col A To: Col B
1 1
2 2
1 3
3
- Словари не принимают дубликаты ключей, просто игнорируют их