Spring4D无法对有序字典进行排序。

4

我需要一个可以排序的字典。我认为Spring4D TOrderedDictionary是实现这一功能的类,但我无法使其正常工作:排序不起作用。

我编写了一个小的测试程序来展示我的问题:

program Spring4DOrderedDictionaryTest;
{$APPTYPE CONSOLE}
{$R *.res}
uses
    System.SysUtils,
    System.Generics.Collections,
    System.Generics.Defaults,
    Spring.Collections;

type
    TKey   = TPair<Double, Integer>;
    TValue = Integer;

    TEqComparer = class(TEqualityComparer<TKey>, IEqualityComparer<TKey>)
        function Equals(const Left, Right: TKey): Boolean; reintroduce;
        function GetHashCode(const Value: TKey): Integer; reintroduce;
    end;

    TAComparer = class(TComparer<TPair<TPair<Double, Integer>, Integer>>, IComparer<TPair<TKey, TValue>>)
        function Compare(const Left, Right: TPair<TKey, Integer>): Integer; override;
    end;

function TEqComparer.Equals(const Left, Right: TKey): Boolean;
begin
    Result := Abs(Left.Key - Right.Key) < 1E-9;
    if Result then
        Result := Left.Value = Right.Value;
end;


function TEqComparer.GetHashCode(const Value: TKey): Integer;
begin
    Result := Round(Value.Key * 1E6) + Value.Value;
end;

function TAComparer.Compare(const Left, Right: TPair<TKey, Integer>): Integer;
begin
    if Abs(Left.Key.Key - Right.Key.Key) < 1E-9 then begin
        if Left.Key.Value < Right.Key.Value then
            Result := -1
        else if Left.Key.Value > Right.Key.Value then
            Result := 1
        else
            Result := 0;
    end
    else begin
        if Left.Key.Key < Right.Key.Key then
            Result := -1
        else if Left.Key.Key > Right.Key.Key then
            Result := 1
        else
            Result := 0;
    end;
end;

const
    SEG_START = 0;
    SEG_END   = 1;
var
    Events      : IOrderedDictionary<TKey, TValue>;
    Event       : TPair<TKey, TValue>;
    EqComparer  : IEqualityComparer<TKey>;
    AComparer   : IComparer<TPair<TKey, TValue>>;
begin
    EqComparer := TEqComparer.Create;
    AComparer  := TAComparer.Create;
    Events     := TCollections.CreateOrderedDictionary<TKey, TValue>;//(EqComparer);

    // Put some data into the dictionary
    Events.Add(TKey.Create(-7.41, SEG_START), 0);
    Events.Add(TKey.Create(-1.3,  SEG_END  ), 0);
    Events.Add(TKey.Create(-4.0,  SEG_START), 1);
    Events.Add(TKey.Create(-4.21, SEG_END  ), 1);
    Events.Add(TKey.Create(-4.92, SEG_START), 2);
    Events.Add(TKey.Create(-4.26, SEG_END  ), 2);
    Events.Add(TKey.Create(-4.55, SEG_START), 3);
    Events.Add(TKey.Create(-2.54, SEG_END  ), 3);
    Events.Add(TKey.Create(-3.70, SEG_START), 4);
    Events.Add(TKey.Create(-3.70, SEG_END  ), 4);

    // Sort the dictionary
    Events.Ordered(AComparer);

    // Show the values in dictionary (Should be sorted -7.41 first and -1.3 last)    
    for Event in Events do
        WriteLn(Format('  %5.2f %d %d', [Event.Key.Key, Event.Key.Value, Event.Value]));

    ReadLn;
end.

上述代码按添加顺序显示值,而不是我认为AComparer应该创建的顺序。
我尝试使用相等比较器创建OrderedDictionary,但没有改变。
感谢任何帮助。

正如我在最近的帖子中所说,你不能期望这两个比较器以任何健壮和有用的方式运作。 - David Heffernan
1个回答

4

首先,您需要知道字典中项目的顺序通常是未定义的,因为它取决于底层数据结构 - 通常是哈希表,因此项目的顺序虽然是确定性的,但看起来“随机”或没有特定顺序。

现在,Spring4d 中有某些字典可以保证项目的顺序。

在1.2.2版本(当前最新版本)中,一般有三种方法可以创建字典:

TCollections.CreateDictionary // internally is just a wrapper around the TDictionary from System.Generics.Collections
TCollections.CreateSortedDictionary // internally uses a red/black tree which makes items ordered - when not explicitly passed it uses the default comparer for the key type
TCollections.CreateOrderedDictionary // internally uses an additional list to preserve items in order they were added to the dictionary

在即将到来的2.0版本(目前是develop分支),有两种方式:
TCollections.CreateDictionary // internally uses a new implementation based on a hashtable which also preserves the order of addition
TCollections.CreateSortedDictionary // same as in 1.2.2 this uses a red/black tree to store items based on the IComparer<TKey>

第二点,您需要知道Ordered方法来自于IEnumerable<T>,它不会修改它被调用的集合,而是返回一个新的IEnumerable<T>,该集合中的项目按您传递给该函数的比较器所确定的顺序排列。

因此,您有两个选择:

  • 使用TCollections.CreateSortedDictionary并传入一个IComparer<TKey> - 请记住,由于红/黑树的工作方式,在改变后决定顺序的值后,顺序可能会被打破。
  • 利用Ordered结果并迭代它。

1
重复的键怎么办? - fpiette
不允许在字典中使用,以下是程序相关的内容。 - Stefan Glienke
我刚刚测试了一下,我的测试表明MultiMap允许这样做。这是正确的吗?至少我的测试数据中添加了一个重复项后,它产生了正确的结果。 - fpiette
1
正确,multimap 就像一个字典 <k,list<v>>。 - Stefan Glienke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接