Dictionary & Collection ?

Liên hệ QC

Hoàng Nhật Phương

Thành viên gắn bó
Tham gia
5/11/15
Bài viết
1,894
Được thích
1,214
Xin chào các Bạn,
Đầu xuân chúc mọi người luôn mạnh khỏe & bình an.
----
Như tiêu đề OT đã nêu, OT có đọc bài viết của Bạn Collection @befaint nhưng có lẽ do kiến thức còn hạn hẹp nên chưa thể hiểu hết về sự khác biệt giữa Dictionary & Collection,
Ví dụ trong 2 đoạn code dưới đây sử dụng Dictionary & Collection cho cùng một công việc:
Mã:
Option Explicit

Sub CollectionFilter()
Dim TT As Double
TT = Timer
Dim myCol As Collection
Set myCol = New Collection
Dim i As Long, lRow As Long, ArrData(), Result(), sKey As String, j As Long
With Sheet1
    lRow = .Range("B" & Rows.Count).End(xlUp).Row
    ArrData = .Range("B2:D" & lRow).Value2
    lRow = UBound(ArrData, 1)
    ReDim Result(1 To lRow, 1 To 4)
    For i = 1 To lRow
        sKey = ArrData(i, 1)
        If sKey <> "" Then
            If KeyExists(myCol, sKey) = False Then
                j = j + 1
                myCol.Add j, sKey
                Result(j, 1) = j
                Result(j, 2) = sKey
                Result(j, 3) = ArrData(i, 2)
                Result(j, 4) = ArrData(i, 3)
            Else
                Result(myCol.Item(sKey), 4) = Result(myCol.Item(sKey), 4) + ArrData(i, 3)
             End If
        End If
    Next i
    If j > 0 Then
        .Range("M2").Resize(100, 4).ClearContents
        .Range("M2").Resize(j, 4) = Result
        .Range("Q7") = Timer - TT
    End If
End With


End Sub


Sub DictionaryFilter()
Dim TT As Double
TT = Timer

Dim Dic As Object
Dim i As Long, lRow As Long, ArrData(), Result(), sKey As String, j As Long
Set Dic = CreateObject("Scripting.Dictionary")
With Sheet1
    lRow = .Range("B" & Rows.Count).End(xlUp).Row
    ArrData = .Range("B2:D" & lRow).Value2
    lRow = UBound(ArrData, 1)
    ReDim Result(1 To lRow, 1 To 4)
    For i = 1 To lRow
        sKey = ArrData(i, 1)
        If sKey <> "" Then
            If Not Dic.Exists(sKey) Then
                j = j + 1
                Dic.Add sKey, j
                Result(j, 1) = j
                Result(j, 2) = sKey
                Result(j, 3) = ArrData(i, 2)
                Result(j, 4) = ArrData(i, 3)
            Else
                Result(Dic.Item(sKey), 4) = Result(Dic.Item(sKey), 4) + ArrData(i, 3)
            End If
        End If
    Next i
    If j > 0 Then
        .Range("H2").Resize(100, 4).ClearContents
        .Range("H2").Resize(j, 4) = Result
        .Range("L7") = Timer - TT
    End If
End With

End Sub
Trên máy tính của OT có vẻ như Collection nhanh hơn Dictionary :
1613618736712.png

Nhờ các bạn chỉ dẫn cho OT hiểu rõ thêm trong trường hợp nào thì dùng cái nào sẽ tối ưu hơn?
Tại sao Collection nhanh hơn Dictionary mà không thấy mọi người sử dụng Collection mà thường sử dụng Dictionary hơn hay do thói quen ạ?
 

File đính kèm

  • Collection.xlsb
    1.2 MB · Đọc: 13
Lần chỉnh sửa cuối:
Dic tuy nhanh hơn trước nhưng vẫn thua xa col Chú ạ:
Lại so sánh khập khiễng: Code bài 28 là 50 ngàn dữ liệu, 12 keys, code test của mi là 66 ngàn với 33 ngàn keys, tỷ lệ quá khác biệt
Ngoài ra phải đọc lại bài trên của bác @batman1 về phương pháp test và tính năng, và trên nữa của bác @VetMini về kiểu của keys: Nếu key là String và dài thì thế nào, thậm chí có khi phải dùng thứ khác như HashTable, ...
 
Lần chỉnh sửa cuối:
Upvote 0
Ngoài ra phải đọc lại bài trên của bác @batman1 về phương pháp test và tính năng, và trên nữa của bác @VetMini về kiểu của keys: Nếu key là String và dài thì thế nào, thậm chí có khi phải dùng thứ khác như HashTable, ...
Tôi nghĩ là độ dài key cũng không đáng ngại. Cái quan trọng là có tất cả bao nhiêu dữ liệu (key) và trong đó số key duy nhất là bao nhiêu.

Như trong bài #5 ta có:
Mã:
Private Const maxCount = 1048500
Private Const prefixKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_"
...
For i = 1 To maxCount
    sKey = prefixKey & i
Tức hơn 1 triệu key và cũng là hơn 1 triệu key duy nhất, có độ dài từ 28 (i = 1..9) tới 34 ký tự. Bài toán với hơn 1 triệu key duy nhất tôi thấy có vẻ ít gặp trong cuộc sống văn phòng. Nếu ta sửa thành sKey = prefixKey & (i Mod 100000), tức hơn 1 triệu key nhưng số key duy nhất là 100 000, và vẫn độ dài ấy thì có lẽ đít thon ít mỡ sẽ cho collection biết thế nào là lễ độ.

Mọi người thử xem sao nhé.
 
Upvote 0
@Hoàng Nhật Phương :
Quan trọng là thuật toán, và thói quen sử dụng cái nào thì dùng cái đó
Và tùy thuộc vào trường hợp cụ thể, không phải ngẫu nhiên mà Họ lập ra và tồn tại cả 2 thứ này Dictionary , Collection...
Nên câu hỏi mãi cũng là câu hỏi tiếp tục...

Nhưng tóm lại cả 2 chúng chỉ là công cụ mà thôi, sử dụng công cụ cho trường hợp nào, cách nào mới cần, hơn là so sánh cái nào hơn cái nào.
Với các bài toán ở diễn đàn này thì toàn nhỏ và khá nhỏ nên dùng công cụ nào cũng không khác nhau nhiều, nên quen gì dùng đó thôi, chưa kể có khi code kiểu dùng 1 lần (dành cho người hỏi) rồi bỏ, hoặc cả tháng cả năm sau mới dùng lại, ... thì chi phí thời gian băn khoăn chọn công cụ nào chỉ làm lãng phí cũng như vô ích.
 
Upvote 0
...
Theo tôi bạn nên tạm gác chuyện tốc độ sang một bên. Nếu tôi hiểu thì bạn chưa hiểu hết collection, dictionary. Vậy thì trước hết hãy hiểu chúng, sử dụng chúng thuần thục. Còn khi gặp bài toán cụ thể mà bạn thấy dictionary chậm thì lúc đó tìm cách khác cũng chả bao giờ muộn.

1. Collection:
- key không thể là số (không quan trọng lắm), không thể là OBJECT (nhược điểm lớn)
- Chỉ có Remove, không thể xóa hết các item, key (chỉ còn nước hủy đối tượng và tạo lại)
- không có phương thức Exists, Items, Keys
- không có thuộc tính Key
- không dùng được trong các tập tin VBS (dùng VB Script)
- với key không phân biệt hoa thường. Tức nếu đã có key là "Hic hic" thì không thể thêm key mới là "hic hic".

2. Dictionary:
- key có thể là chuỗi, số và OBJECT (vd. là đít sần khác)
- có Remove và RemoveAll
- có phương thức Exists, Items, Keys
- có thuộc tính Key cho phép thay đổi giá trị key đang tồn tại.
- dùng được trong các tập tin VBS (dùng VB Script)
- có thuộc tính CompareMode nên có thể chủ động phân biệt hoa thường hay không.
...
Cảm ơn Bác Siwtom đã góp ý và chỉ dẫn chi tiết cho con ạ.
Dạ đúng rồi Bác trước hết con cũng nghĩ là nên làm vậy ạ.
------
Xin cảm ơn tất cả mọi người đã góp ý, sự thảo luận nhiều đúng là luôn mang lại những kiến thức hữu ích mặc dù khả năng tiếp thu của OT rất là kém.
Mặc dù chưa hiểu hết về ưu nhước điểm của Dic và Col nhưng qua đây OT cũng đã cảm thấy bản thân mình đã biết sử dụng các công cụ này, OT sẽ cố gắng sử dụng vào các bài toán của mình sắp tới để có thể có thêm kinh nghiệm ạ.
 
Upvote 0
Tôi nghĩ là độ dài key cũng không đáng ngại. Cái quan trọng là có tất cả bao nhiêu dữ liệu (key) và trong đó số key duy nhất là bao nhiêu.
Mọi người thử xem sao nhé.
Vâng anh, tôi cũng nghĩ như vậy nên tôi cũng nói là "xem thế nào", nghĩa là so sánh với cùng 1 số lượng key thì tốc độ cũng phải bị thay đổi theo chiều dài key
Thử theo anh bảo thì thấy với tỷ lệ key/ dữ liệu càng ít thì Dict càng nhanh, 50.000 keys thì Collect hít khói, 10.000 keys thì thậm chí không có khói để hít. Thế mới thấy cần phải test trên diện vừa rộng vừa sâu

1613704097923.png
 
Upvote 0
Ở đây ta nói "Đít Sần" là nói về cái công cụ COM của MS, nằm trong thư viện Script Runtime.

- Vì mục đích chính để hổ trợ Script cho nên nó bắt buộc phải bao những trường hợp cần thiết của Script.
- Vì mục đích chính là tạo một phép áp trực tiếp (map) từ một giá trị (key) sang giá trị khác (item), không hề nề hà kiểu của bên đầu vào lẫn đầu ra cho nên nó bắt buộc phải sử dụng những thuật toán rất phức tạp bên trong. Và những thuật toán này có thể mâu thuẫn nhau về hiệu quả, ví dụ A là thuật toán băm key dạng chuỗi, B là thuật toán băm key dạng số, để A hiệu quả hơn thì phải hy sinh hiệu quả của B; tác giả đít sần bắt buộc phải chọn "75% trường hợp người dùng sẽ dùng key chuỗi, vậy thì chọn ưu tiên A" (*1)

Chú ý vào điều kiện thứ hai kể trên, nó bắt buộc phải dựa trên môi trường làm việc chủ đich (điều kiện thứ nhất) mà cân nhắc các thuật toán bên trong. Nói cách khác, nếu dữ liệu của bạn nằm đúng trường hợp mà nó đã chọn làm tối ưu thì sẽ thấy nó chạy êm như ru.

Mặt khác:
Cờ loét sần ở đây là cấu trúc dữ liệu được cung cấp nội bộ, bên trong môi trường VBA. Và cái loét sần này làm đúng nhiệm vụ của nó: nơi lưu trữ.
- Nó sẽ lưu trữ dữ liệu theo đúng thứ tự thằng nào vào trước thì lấy số 1, thằng kế lấy số 2,... (*2)
- Nó sẽ cho phép bạn dùng một cái id (dạng chuỗi) để 'đánh dấu' từng phần tử.
sheets trong Excel là ví dụ điển hình của loét sần: mỗi sheet có số thứ tự và tên của nó (*3)
Vì môi trường đã chỉ định, và vì điều kiện sử dụng cũng rõ rệt, cho nên MS dễ dàng hơn khi chọn một vùng mượt/sweet spot (*4) cho loét sần.
Rất có thể điều kiện mà quý vị tét trên kia lọt vào vùng mượt của nó.

Ở chủ đề bài này (bài #1), thớt chỉ nói về "ứng dụng thường gặp trong GPE", tức là "lọc duy nhất". Và so sánh này nọ cũng là dựa trên 2 "tôn chỉ của GPE":
1. ứng dụng lọc duy nhất
2. tốc độ chạy
cộng một điều, tuy không phải là "tôn chỉ" nhưng rất thường gặp, coi như là "cố tật của GPE"
3. dữ liệu khủng và méo mó (rất ít khi chuẩn)

(*1) nếu quý vị theo dõi cái đít sần này thường xuyên sẽ nhớ mấy năm trước có vụ tét ra nó làm việc với key chuỗi nhanh hơn key số.

(*2) tuy bạn thường thấy đít sần cũng cho ra thứ tự nhập như vậy, nhưng MS không hề bảo đảm việc này cho đít sần, dựa vào đó mà viết code thì chết có ngày.

(*3) theo hướng đối tượng, cái lớp loét sần mà sheet sử dụng chỉ thừa kế từ lớp loét sần căn bản thôi, chính nó còn còn có nhiều giao diện khác.

(*4) tôi đã từng đề cập vụ "vùng mượt" này nhiều lần. Các nhà viết code luôn luôn phải dùng thống kê để đoán vùng data mà người dùng hay gặp nhất, và viết code ưu tiên cho loại dữ liệu này.
Vì dân GPE hay sử dụng dữ liệu kiểu mà MS đoán không nổi cho nên rất dễ lâm vào tình trạng "nếu muốn nhanh thì phải tự tìm lấy cái thích hợp hơn mà dùng"
 
Lần chỉnh sửa cuối:
Upvote 0
Xin chào các Bạn,
Đầu xuân chúc mọi người luôn mạnh khỏe & bình an.
----
Như tiêu đề OT đã nêu, OT có đọc bài viết của Bạn Collection @befaint nhưng có lẽ do kiến thức còn hạn hẹp nên chưa thể hiểu hết về sự khác biệt giữa Dictionary & Collection,
Ví dụ trong 2 đoạn code dưới đây sử dụng Dictionary & Collection cho cùng một công việc:
Mã:
Option Explicit

Sub CollectionFilter()
Dim TT As Double
TT = Timer
Dim myCol As Collection
Set myCol = New Collection
Dim i As Long, lRow As Long, ArrData(), Result(), sKey As String, j As Long
With Sheet1
    lRow = .Range("B" & Rows.Count).End(xlUp).Row
    ArrData = .Range("B2:D" & lRow).Value2
    lRow = UBound(ArrData, 1)
    ReDim Result(1 To lRow, 1 To 4)
    For i = 1 To lRow
        sKey = ArrData(i, 1)
        If sKey <> "" Then
            If KeyExists(myCol, sKey) = False Then
                j = j + 1
                myCol.Add j, sKey
                Result(j, 1) = j
                Result(j, 2) = sKey
                Result(j, 3) = ArrData(i, 2)
                Result(j, 4) = ArrData(i, 3)
            Else
                Result(myCol.Item(sKey), 4) = Result(myCol.Item(sKey), 4) + ArrData(i, 3)
             End If
        End If
    Next i
    If j > 0 Then
        .Range("M2").Resize(100, 4).ClearContents
        .Range("M2").Resize(j, 4) = Result
        .Range("Q7") = Timer - TT
    End If
End With


End Sub


Sub DictionaryFilter()
Dim TT As Double
TT = Timer

Dim Dic As Object
Dim i As Long, lRow As Long, ArrData(), Result(), sKey As String, j As Long
Set Dic = CreateObject("Scripting.Dictionary")
With Sheet1
    lRow = .Range("B" & Rows.Count).End(xlUp).Row
    ArrData = .Range("B2:D" & lRow).Value2
    lRow = UBound(ArrData, 1)
    ReDim Result(1 To lRow, 1 To 4)
    For i = 1 To lRow
        sKey = ArrData(i, 1)
        If sKey <> "" Then
            If Not Dic.Exists(sKey) Then
                j = j + 1
                Dic.Add sKey, j
                Result(j, 1) = j
                Result(j, 2) = sKey
                Result(j, 3) = ArrData(i, 2)
                Result(j, 4) = ArrData(i, 3)
            Else
                Result(Dic.Item(sKey), 4) = Result(Dic.Item(sKey), 4) + ArrData(i, 3)
            End If
        End If
    Next i
    If j > 0 Then
        .Range("H2").Resize(100, 4).ClearContents
        .Range("H2").Resize(j, 4) = Result
        .Range("L7") = Timer - TT
    End If
End With

End Sub
Trên máy tính của OT có vẻ như Collection nhanh hơn Dictionary :
View attachment 254243

Nhờ các bạn chỉ dẫn cho OT hiểu rõ thêm trong trường hợp nào thì dùng cái nào sẽ tối ưu hơn?
Tại sao Collection nhanh hơn Dictionary mà không thấy mọi người sử dụng Collection mà thường sử dụng Dictionary hơn hay do thói quen ạ?
Nếu so sánh về tốc độ với ADO thì ADO sẽ không thể so bì kịp với bài toán này. Tuy nhiên được cái nó dễ chỉnh sửa và bảo trì code.

1613718861883.png
 
Upvote 0
Nếu so sánh về tốc độ với ADO thì ADO sẽ không thể so bì kịp với bài toán này. Tuy nhiên được cái nó dễ chỉnh sửa và bảo trì code.

View attachment 254308
Anh đã khơi khơi ra rồi thì ít ra cũng phải show code ra cho cho OT chứ Anh (mặc dù chủ đề không liên quan gì đến ADO ạ),
với khả năng của OT là cứ ví dụ & kèm code thực tế thì mới có vừa ý Anh ạ, :p
 
Upvote 0
Ẹc ... ẹc ... với dữ liệu vài chục dòng thì sử dụng For ... Next cho nó đơn giản, chứ vài trăm vài ngàn dòng có mà chết!

Bài dưới đây tôi hướng dẫn bạn sử dụng ComboBox thật tiện lợi nè:

Mã:
Sub TestLocDuyNhat()
    Dim arrData
    Dim cbxTemp As MSForms.ComboBox
    Dim e As Long, n As Long, r As Long, u As Long
    Set cbxTemp = UserForm1.ComboBox1
    cbxTemp.Clear
    Sheet1.Columns("F:G").ClearContents
    e = Sheet1.Range("A" & Rows.Count).End(xlUp).Row
    arrData = Sheet1.Range("A1:B" & e).Value
    u = UBound(arrData)
    For r = 1 To u
        cbxTemp.Text = arrData(r, 1)
        If Not cbxTemp.MatchFound Then
            cbxTemp.AddItem arrData(r, 1), n
            cbxTemp.List(n, 1) = arrData(r, 2)
            n = n + 1
        Else
            cbxTemp.List(cbxTemp.ListIndex, 1) = cbxTemp.List(cbxTemp.ListIndex, 1) & " + " & arrData(r, 2)
        End If
    Next
    Sheet1.Range("F1:G" & cbxTemp.ListCount).Value = cbxTemp.List
End Sub
Code này chạy nhanh nè, thử 5000 key tôi cảm thấy chạy rất ngọt ngào, khi nhìn code thấy .listindex tôi không nghĩ code có thể chạy mượt như vậy, và khi năng lên 10000 key thì tôi cảm thấy nó không mượt mà như trước nữa. Code này có điểm cộng lớn, nếu hiểu bản chất mà trau truốt thêm tôi nghĩ sẽ rất tiềm năng.
 
Upvote 0
Anh đã khơi khơi ra rồi thì ít ra cũng phải show code ra cho cho OT chứ Anh (mặc dù chủ đề không liên quan gì đến ADO ạ),
với khả năng của OT là cứ ví dụ & kèm code thực tế thì mới có vừa ý Anh ạ, :p
Code group bình thường thôi em, tuy nhiên nó không giống với kết quả của em ở chỗ là nó đã sắp xếp. Còn code của em thì nó vẫn giữ nguyên. Nếu như code của em thêm vào cái sắp xếp nữa thì nó sẽ khớp với dùng ADO của anh.
 
Upvote 0
Upvote 0
Code group bình thường thôi em, tuy nhiên nó không giống với kết quả của em ở chỗ là nó đã sắp xếp. Còn code của em thì nó vẫn giữ nguyên. Nếu như code của em thêm vào cái sắp xếp nữa thì nó sẽ khớp với dùng ADO của anh.
là sao Anh chắc anh đang nhầm với đề tài bên kia rồi
hú hú @!>><
 
Upvote 0
Dạ Anh, sau khi anh Tuân cũng nói về group OT mới nghĩ chắc OT nhầm ạ.
Code của anh như sau:
Mã:
Sub Test()
    Dim t As Double
    t = Timer
    With CreateObject("ADODB.Recordset")
        .Open " Select [Code],MIN([Date]),sum(Quantity) from [A1:D] Group By [Code]", "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & ThisWorkbook.FullName & ";Extended Properties=Excel 12.0"
        Sheet1.Range("I12").CopyFromRecordset .DataSource
        Sheet1.Range("L12") = Timer - t
    End With
End Sub
 
Upvote 0
Code của anh như sau:
Mã:
Sub Test()
    Dim t As Double
    t = Timer
    With CreateObject("ADODB.Recordset")
        .Open " Select [Code],MIN([Date]),sum(Quantity) from [A1:D] Group By [Code]", "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & ThisWorkbook.FullName & ";Extended Properties=Excel 12.0"
        Sheet1.Range("I12").CopyFromRecordset .DataSource
        Sheet1.Range("L12") = Timer - t
    End With
End Sub
Cảm ơn Anh đã chỉ thêm một cách nữa, OT test rồi code không báo lỗi.
Còn mọi cái OT bàn gì nữa ạ..ở đây xung quanh toàn là 'Cây cổ thụ' với 'Núi cao' nên OT chỉ đứng ngồi ngắm và chiêm ngưỡng thưởng thức thôi ạ. :xmaslaugh:
 
Upvote 0
Ở đây ta nói "Đít Sần" là nói về cái công cụ COM
Dic dùng bảng băm. Nếu vậy sự tương đồng của các key cũng sẽ anh hưởng tới dic đúng không bác. Ví dụ cặp key( ac, ab) ( hai cái này giống nhau chũ a)sẽ khác tryowngf hợp cặp key( ac, df)
 
Upvote 0
Web KT

Bài viết mới nhất

Back
Top Bottom