Приветствую! Заметил ещё одно очень странное поведение словарей — скорость заполнения/проверки наличия сильно зависит от порядка ключей в массиве. Отсортированные ключи добавляются/проверяются намного быстрее (~ 6 раз на примере). Для сравнения добавил карты Виталия из библы — тоже есть различие, но на уровне погрешности.
Option Base 1
Option Explicit
Option Private Module
'==================================================================================================
'Dim BV As New BedvitCOM.VBA
'==================================================================================================
Sub PRDX_SortRecur_WithInd(aV(), aI() As Long, LBnd&, UBnd&)
Dim i&, j&, n&, x, y
i = LBnd: j = UBnd: x = aV((LBnd + UBnd) \ 2)
Do
While (aV(i) < x): i = i + 1: Wend
While (x < aV(j)): j = j - 1: Wend
If (i <= j) Then
y = aV(i): aV(i) = aV(j): aV(j) = y
n = aI(i): aI(i) = aI(j): aI(j) = n
i = i + 1: j = j - 1
End If
Loop Until (i > j)
If (LBnd < j) Then PRDX_SortRecur_WithInd aV, aI, LBnd, j
If (i < UBnd) Then PRDX_SortRecur_WithInd aV, aI, i, UBnd
End Sub
'==================================================================================================
'==================================================================================================
'==================================================================================================
Function PRDX_Sort_Arr2D(a2D, Optional ByVal nCol& = 1) As Variant
Dim aNew, aVal(), aInd&(), r&, rr&, c&
r = UBound(a2D, 1)
ReDim aVal(r), aInd(r)
For r = 1 To UBound(aInd)
aInd(r) = r
aVal(r) = a2D(r, nCol)
Next r
PRDX_SortRecur_WithInd aVal, aInd, 1, UBound(aInd)
ReDim aNew(UBound(a2D, 1), UBound(a2D, 2)): r = 0
For r = 1 To UBound(a2D, 1)
rr = aInd(r)
For c = 1 To UBound(a2D, 2)
aNew(r, c) = a2D(rr, c)
Next c
Next r
PRDX_Sort_Arr2D = aNew
End Function
'==================================================================================================
'==================================================================================================
'==================================================================================================
'==================================================================================================
Sub CreateArr2Col(aOut, nUniq&, nRepeats&, Optional UnSort As Boolean)
Dim n&, r&, p&
If UnSort Then
ReDim aOut(nUniq * (1 + nRepeats), 3) ' Key, Value, Sort(Optional Temp)
Else
ReDim aOut(nUniq * (1 + nRepeats), 2)
End If
For n = 1 To nUniq
r = r + 1
aOut(r, 1) = "Key_" & n
aOut(r, 2) = "Val_" & r
If UnSort Then aOut(r, 3) = Rnd()
For p = 1 To nRepeats
r = r + 1
aOut(r, 1) = aOut(r - 1, 1)
aOut(r, 2) = "Val_" & r
If UnSort Then aOut(r, 3) = Rnd()
Next p
Next n
If Not UnSort Then Exit Sub
aOut = PRDX_Sort_Arr2D(aOut, 3)
'BV.ArraySortV aOut, 3
ReDim Preserve aOut(UBound(aOut), 2)
End Sub
'==================================================================================================
'==================================================================================================
'==================================================================================================
Private Sub Test_Add()
Dim dic As New Dictionary
Dim x, a, t!, r&
' UnSort ----------------------------------------
t = Timer
CreateArr2Col a, 50000, 100, True
Debug.Print Round(Timer - t, 2), "CreateArr(UnSort)" ' 14.3
t = Timer
For r = 1 To UBound(a, 1)
x = dic(a(r, 1))
Next r
Debug.Print Round(Timer - t, 2), "Test(UnSort)" ' 2.9
dic.RemoveAll
' Sort ------------------------------------------
t = Timer
CreateArr2Col a, 50000, 100
Debug.Print Round(Timer - t, 2), "CreateArr(Sort)" ' 2.6
t = Timer
For r = 1 To UBound(a, 1)
x = dic(a(r, 1))
Next r
Debug.Print Round(Timer - t, 2), "Test(Sort)" ' 0.5
End Sub
'==================================================================================================
Private Sub Test_Exists()
Dim dic As New Dictionary
Dim a, s$, t!, r&
' UnSort ----------------------------------------
t = Timer
CreateArr2Col a, 50000, 100, True
Debug.Print Round(Timer - t, 2), "CreateArr(UnSort)" ' 14.3
t = Timer
For r = 1 To UBound(a, 1)
s = a(r, 1)
If Not dic.Exists(s) Then dic.Add s, 0
Next r
Debug.Print Round(Timer - t, 2), "Test(UnSort)" ' 3.1
'Debug.Print UBound(a, 1)
dic.RemoveAll
' Sort ------------------------------------------
t = Timer
CreateArr2Col a, 50000, 100
Debug.Print Round(Timer - t, 2), "CreateArr(Sort)" ' 2.6
t = Timer
For r = 1 To UBound(a, 1)
s = a(r, 1)
If Not dic.Exists(s) Then dic.Add s, 0
Next r
Debug.Print Round(Timer - t, 2), "Test(Sort)" ' 0.7
'Debug.Print UBound(a, 1)
End Sub
'==================================================================================================
Private Sub Test_ResumeNext()
Dim dic As New Dictionary
Dim a, t!, r&
On Error Resume Next
' UnSort ----------------------------------------
t = Timer
CreateArr2Col a, 50000, 100, True
Debug.Print Round(Timer - t, 2), "CreateArr(UnSort)" ' 14.6
t = Timer
For r = 1 To UBound(a, 1)
dic.Add a(r, 1), 0
Next r
Debug.Print Round(Timer - t, 2), "Test(UnSort)" ' 6.7
'Debug.Print UBound(a, 1)
dic.RemoveAll
' Sort ------------------------------------------
t = Timer
CreateArr2Col a, 50000, 100
Debug.Print Round(Timer - t, 2), "CreateArr(Sort)" ' 2.5
t = Timer
For r = 1 To UBound(a, 1)
dic.Add a(r, 1), 0
Next r
Debug.Print Round(Timer - t, 2), "Test(Sort)" ' 4.2
'Debug.Print UBound(a, 1)
On Error GoTo 0
End Sub
'==================================================================================================
' Need BedVit.COM library: https://bedvit.ru/com/
Private Sub Test_Map()
Dim map As New BedvitCOM.UnorderedMap
Dim a, t!, r&
On Error Resume Next
' UnSort ----------------------------------------
t = Timer
CreateArr2Col a, 50000, 100, True
Debug.Print Round(Timer - t, 2), "CreateArr(UnSort)" ' 14.4
t = Timer
For r = 1 To UBound(a, 1)
map.Insert a(r, 1), 0
Next r
Debug.Print Round(Timer - t, 2), "Test(UnSort)" ' 1.6
'Debug.Print UBound(a, 1)
map.Clear
' Sort ------------------------------------------
t = Timer
CreateArr2Col a, 50000, 100
Debug.Print Round(Timer - t, 2), "CreateArr(Sort)" ' 2.5
t = Timer
For r = 1 To UBound(a, 1)
map.Insert a(r, 1), 0
Next r
Debug.Print Round(Timer - t, 2), "Test(Sort)" ' 1.4
'Debug.Print UBound(a, 1)
On Error GoTo 0
End Sub
'==================================================================================================
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Jack Famous, привет. Я проверял на случайных данных, и там Коллекция оказывалась значительно быстрее на объемах 500 + тыс. Что впрочем объяснимо таким моментом, что у хеш таблицы (по сути это массив) dictionary четко фиксированый размер (~1000 элементов). Но если была бы возможность регулировать размер этой таблицы, то можно было бы получить бОльшую скорость dictionary на больших объемах. Скажу даже, я проверял сие, брал реализацию словаря The trick-а, делал в ней настраиваемый размер хеш-таблицы, и моя реализация в виде класса vba оказывалась быстрее Dictionary на объемах 500 т.+, что впроче все еще было медленей коллекции. В общем интересные моменты есть, хотя в сравнении, с UnorderedMap это все это конечно выглядит игрушками.. Если говорить о сортировке, то в коллекции, по логике она должна делать только хуже. Ведь коллекция использует логическое дерево, и при добавлении сортированых данных, постоянно будет расти только одна ветвь и получится так, то к самым первым элементам будет самый быстрый доступ, к последним - самый медленный..
testuser, привет! Спасибо за внимание! Ну, тут не сравнение методов а демонстрация того, что сортировка ключей сильно влияет на скорость наполнения словаря. Совсем неочевидный нюанс, о котором я (да и, наверное, многие, если не все) не знал.
Цитата
testuser: Коллекция оказывалась значительно быстрее на объемах 500 + тыс.
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄