2010-03-27 30 views
16

Tôi có thể sử dụng TEnumerator để đi qua TDictionary của tôi theo thứ tự sắp xếp theo khóa như thế nào?Làm thế nào tôi có thể liệt kê một TDictionary trong thứ tự chữ cái bằng khóa trong Delphi 2009?

Tôi đã có một cái gì đó như thế này:

var 
    Dic: TDictionary<string, string>; 
    Enum: TPair<string, string>; 

    begin 
    Dic := TDictionary<string, string>.create; 
    Dic.Add('Tired', 'I have been working on this too long'); 
    Dic.Add('Early', 'It is too early in the morning to be working on this'); 
    Dic.Add('HelpMe', 'I need some help'); 
    Dic.Add('Dumb', 'Yes I know this example is dumb'); 

    { I want to do the following but do it in sorted order by Enum.Key } 
    for Enum in Dic do 
     some processing with Enum.Key and Enum.Value; 

    Dic.Free; 
    end; 

Vì vậy, tôi muốn để xử lý từ điển của tôi theo thứ tự: Dumb, sớm, HelpMe, mệt mỏi.

Thật không may sự giúp đỡ Delphi là rất tối thiểu trong mô tả cách điều tra viên nói chung và TEnumerator cụ thể hoạt động và không đưa ra ví dụ mà tôi có thể tìm thấy. Ngoài ra còn có rất ít được viết trên web về việc sử dụng Enumerators with Generics trong Delphi.

Và mã mẫu của tôi ở trên thậm chí không sử dụng Bộ đếm số, vì vậy tôi nhầm lẫn về cách thiết kế này được thiết kế để sử dụng.


Cảm ơn Barry, cho câu trả lời của bạn.

Việc mạo hiểm của tôi vào Generics vì tôi đã đặt câu hỏi thú vị. Tôi muốn bắt đầu thực hiện chúng trong mã của tôi. Vấn đề "phân loại" hơi phức tạp, vì dường như Generics dường như có các phương thức xử lý phân loại được xây dựng, nhưng không có ví dụ hay tài liệu hay về cách thực hiện nó.

Cuối cùng, tôi đã làm những gì Barry đề xuất và xây dựng một chỉ mục bên ngoài thành Từ điển. Tuy nhiên, nó không cảm thấy đúng.

Tuy nhiên, sau đó tôi đã có một bất ngờ khác: Tôi đã cố gắng thay thế Gabr 's GPStringHash bằng TDictionary của Generic. Các mã đã được một chút sạch hơn với Generics. Nhưng điểm mấu chốt là TDictionary chậm hơn 3 lần so với Gabr. 1.704,667 cuộc gọi tới TryGetValue mất 45 giây, nhưng hoạt động tương tự đối với các thói quen của Gabr mất 12 giây. Tôi không chắc chắn tại sao, nhưng có lẽ nó đơn giản như Gabr có một hàm Hash nhanh hơn và kết hợp thay đổi. Hoặc có lẽ các generics đã phải khái quát hóa cho mọi trường hợp và vốn đã làm chậm nó xuống.

Không bao giờ-ít hơn, có thể Barry hoặc các nhà phát triển Delphi khác nên xem xét điều này, bởi vì một lần tăng tốc 3 lần cuối cùng có thể có lợi cho tất cả mọi người. Cá nhân tôi sẽ sớm sử dụng những gì được xây dựng trong ngôn ngữ hơn là một gói bên thứ 3 (thậm chí là một gói tốt như Gabr) nếu được lựa chọn. Nhưng bây giờ, tôi sẽ dính vào GPStringHash.

+0

Đây là lần theo dõi: Đầu năm nay (2016), tôi đã nâng cấp lên Delphi XE8. Tôi đã nghĩ rằng TDictionary trong gói Delphi Generics có thể đã được cải thiện kể từ khi tôi hỏi câu hỏi này 5 năm trước đây. Vì vậy, tôi đã lấy GPStringHash bởi @gabr và thay thế bằng TDictionary. Sự chậm lại cho chương trình của tôi là khá đáng kể. Vì vậy, ít nhất là cho tương lai gần, tôi gắn bó với GPStringHash. – lkessler

+1

Tùy thuộc vào nhu cầu của bạn, thay vì duy trì dữ liệu trong từ điển và thỉnh thoảng phân loại dữ liệu, bạn có thể làm ngược lại: duy trì dữ liệu trong danh sách được sắp xếp, nhưng sử dụng từ điển để truy cập nhanh 'ngẫu nhiên'. Điều này phù hợp hơn nếu bạn đang lưu/lấy thông qua SQL, ví dụ. Chỉ cần luôn luôn rõ ràng ai sở hữu đối tượng (dây, bạn an toàn). –

Trả lời

5

Trong trường hợp của tôi, tôi sử dụng TDictionary < String, String>. TKeyCollection lớp học.

function compareKey(const L, R: String): Integer; 
begin 
    Result := SysUtils.CompareText(L, R); 
end; 

function getReverseSortedKeyArray(dictionary: TDictionary<String, String): TArray<String>; 
var 
    keyArray: TArray<String>; 
    keyCollecttion: TDictionary<String, String>.TKeyCollection; 
begin 
    keyCollecttion:= TDictionary<String, String>.TKeyCollection.Create(dictionary); 
    try 
    keyArray:= valueCollecttion.ToArray; 
    TArray.Sort<String>(keyArray, TComparer<String>.Construct(compareKey)); 
    finally 
    keyCollecttion.Free; 
    end; 

    Result := keyArray; 
end; 

Ví dụ về sử dụng:

var 
    key: String; 
    keyArray : TArray<String>; 
begin 
    keyArray := getSortedKeyArray (dictionary); 
    for key in keyArray do 
    begin 
     // ... 
    end; 
end; 
19

Từ điển là bảng băm, do đó, nó không lưu trữ các mục theo thứ tự được sắp xếp. TEnumerator là đơn giản - nó chỉ là một phương tiện lặp qua các mục.

Để nhận các mục theo thứ tự, bạn cần sắp xếp chúng. Một cách sẽ được đưa vào một danh sách và sắp xếp danh sách, như thế này:

var 
    list: TList<string>; 
begin 
    list := TList<string>.Create(Dic.Keys); 
    try 
    list.Sort; 
    // process sorted list of items now 
    finally 
    list.Free; 
    end; 
end; 
+0

Vâng vâng, nó đủ dễ dàng để duy trì một TList riêng biệt là một chỉ số được sắp xếp của các phím. Nhưng trong Generics.Collections, có phương thức DoMoveNext của TEnumerator được cho là để định nghĩa thứ tự sắp xếp, và MoveNext và DoMoveNext (sự khác biệt là gì?) Được cho là cho phép Enumeration of Collections. Điều này không cho phép tôi liệt kê trực tiếp bộ sưu tập mà không cần phải tạo và duy trì một chỉ mục riêng biệt? Đó là những thứ được giải thích rất ít ở khắp mọi nơi. – lkessler

+0

@Barry: p.s. Tôi đã thấy câu trả lời của bạn trong: http://stackoverflow.com/questions/1230054/why-does-tenumerablet-use-pass-through-methods và tôi vui vì bạn đã viết TEnumerable cho hiệu suất đầu tiên. Tôi là một trong những người có chương trình cần mọi tốc độ nó có thể nhận được. Bây giờ tôi đang cố gắng chuyển sang các bảng băm dựa trên bảng băm của Delphi 2009. – lkessler

+0

... và khi tôi điều tra thêm, tôi thấy tất cả các bộ so sánh được xác định trong Generics.Defaults. Chúng trông hữu ích, nhưng chúng là gì nếu chúng ta chỉ để duy trì một chỉ số TList riêng biệt? – lkessler

4

Dưới đây là một số mẫu mã mà sắp xếp qua Array<T> hoặc một TList<T>. Nó duy trì mối quan hệ Cặp giá trị khóa và nó cũng có thể được tinh chỉnh để sắp xếp theo Giá trị thay vì Khóa. Ngoài ra, nó sử dụng một phương pháp ẩn danh để thực hiện việc sắp xếp.

Đảm bảo bao gồm Generics.CollectionsGenerics.Defaults trong mệnh đề của bạn. Phương pháp đầu tiên để sắp xếp sử dụng TArray<T>:

procedure TestSortDictionaryViaArray; 
var 
    D: TDictionary<string, Integer>; 
    A: TArray<TPair<string, Integer>>; 
    P: TPair<string, Integer>; 
begin 
    D := TDictionary<string, Integer>.Create; 

    D.Add('Test - 6', 6); 
    D.Add('Test - 1', 1); 
    D.Add('Test - 0', 0); 
    D.Add('Test - 4', 4); 
    D.Add('Test - 3', 3); 
    D.Add('Test - 5', 0); 
    D.Add('Test - 2', 2); 

    A := D.ToArray; 

    TArray.Sort<TPair<string, Integer>>(A, 
    TComparer<TPair<string, Integer>>.Construct(
     function (const L, R: TPair<string, Integer>): Integer 
     begin 
     Result := CompareStr(L.Key, R.Key); 
     end) 
); 

    for P in A do 
    ShowMessage(P.Key); 
    D.Free; 
end; 

Và điều này đang sử dụng TList<T>:

procedure TestSortDictionaryViaList; 
var 
    D: TDictionary<string, Integer>; 
    L: TList<TPair<string, Integer>>; 
    P: TPair<string, Integer>; 
begin 
    D := TDictionary<string, Integer>.Create; 

    D.Add('Test - 6', 6); 
    D.Add('Test - 1', 1); 
    D.Add('Test - 0', 0); 
    D.Add('Test - 4', 4); 
    D.Add('Test - 3', 3); 
    D.Add('Test - 5', 0); 
    D.Add('Test - 2', 2); 

    L := TList<TPair<string, Integer>>.Create(D); 

    L.Sort(
    TComparer<TPair<string, Integer>>.Construct(
     function (const L, R: TPair<string, Integer>): Integer 
     begin 
     Result := CompareStr(L.Key, R.Key); 
     end) 
); 

    for P in L do 
    ShowMessage(P.Key); 

    D.Free; 
    L.Free; 
end; 

thông tin bổ sung (và không cần thiết): Phương pháp TList<T> cần danh sách được trả tự do, trong khi TArray<T> không cần giải phóng. Trong nội bộ, TList<T> sử dụng TArray<T> (ví dụ: TArray có phương thức lớp học BinarySearch()TList<T> có phương pháp Tìm kiếm nhị phân).

Các vấn đề liên quan