在 delphi 中具有索引访问的 TDictionary

TDictionary with index access in delphi

我需要一个可以通过键或索引访问的列表。 Delphi XE 中是否有任何对象已经为我处理了这个?

我正在尝试使用 TDictionary,但没有按索引访问。

我没有得到相同的顺序,使用 ToArray,例如:

procedure TForm1.Button1Click(Sender: TObject);
var list: TDictionary<string, string>;
    arr: TArray<string>;
    b,a: Integer;
    value: string;
begin
  list := TDictionary<string, string>.Create();
  for a:=0 to 10 do
    begin
       b:=random(1000);
       value := 'index: ' + inttostr(a) + ' - value: ' + inttostr(b);
       list.Add(inttostr(b), value);
       memo1.Lines.Add(value);
    end;
  arr := list.Values.ToArray();
  for value in arr do
  begin
    memo2.Lines.Add(value);
  end;
end;

结果:

index: 0 - value: 830
index: 1 - value: 265
index: 2 - value: 964
index: 3 - value: 765
index: 4 - value: 917
index: 5 - value: 826
index: 6 - value: 353
index: 7 - value: 431
index: 8 - value: 837
index: 9 - value: 373
index: 10 - value: 805

index: 7 - value: 431
index: 8 - value: 837
index: 9 - value: 373
index: 10 - value: 805
index: 4 - value: 917
index: 5 - value: 826
index: 0 - value: 830
index: 2 - value: 964
index: 3 - value: 765
index: 6 - value: 353
index: 1 - value: 265

您可能必须创建自己的 T/IOrderedDictionary class/interface

界面可能如下所示:

Based on Spring4D - IDictionary/IList

unit OrderedDictionary;

interface

uses
  Spring,
  Generics.Collections,
  Generics.Defaults,
  Spring.Collections,
  Spring.Collections.Dictionaries,
  Spring.Collections.Lists;

type
  IOrderedDictionary<K,V> = interface(IDictionary<K,V>)
    function Add(const item: TPair<K,V>): Integer;

  {$REGION 'Property Accessors'}
    function GetCapacity: Integer;
    function GetCount: Integer;
    function GetItem(index: Integer): TPair<K,V>;
    procedure SetCapacity(value: Integer);
    procedure SetCount(value: Integer);
    procedure SetItem(index: Integer; const item: TPair<K,V>);
  {$ENDREGION}

    /// <summary>
    ///   Inserts an item to the IList&lt;TPair<K,V>&gt; at the specified index.
    /// </summary>
    /// <param name="index">
    ///   The zero-based index at which item should be inserted.
    /// </param>
    /// <param name="item">
    ///   The element to insert into the IList&lt;TPair<K,V>&gt;.
    /// </param>
    procedure Insert(index: Integer; const item: TPair<K,V>);
    procedure InsertRange(index: Integer; const values: array of TPair<K,V>); overload;
    procedure InsertRange(index: Integer; const collection: IEnumerable<TPair<K,V>>); overload;

    /// <summary>
    ///   Removes the item at the specified index.
    /// </summary>
    /// <param name="index">
    ///   The zero-based index of the item to remove.
    /// </param>
    /// <exception cref="ArgumentOutOfRangeException">
    ///   <i>index</i> is not a valid index in the IList&lt;TPair<K,V>&gt;.
    /// </exception>
    procedure Delete(index: Integer);
    procedure DeleteRange(index, count: Integer);

    /// <summary>
    ///   Extracts the item at the specified index.
    /// </summary>
    /// <param name="index">
    ///   The zero-based index of the item to extract.
    /// </param>
    /// <exception cref="ArgumentOutOfRangeException">
    ///   <i>index</i> is not a valid index in the IList&lt;TPair<K,V>&gt;.
    /// </exception>
    function ExtractAt(index: Integer): TPair<K,V>;
    function ExtractRange(index, count: Integer): TArray<TPair<K,V>>; overload;

    /// <summary>
    ///   Creates a new list that contains a range of the elements in the
    ///   original list.
    /// </summary>
    /// <param name="index">
    ///   The zero-based index at which the range starts.
    /// </param>
    /// <param name="count">
    ///   The number of elements in the range.
    /// </param>
    /// <remarks>
    ///   If the list contains reference types the elements in the returned
    ///   list point to the same instance as the elements in the original list.
    ///   Also if the original list is a <see cref="Spring.Collections.Lists|TObjectList&lt;TPair<K,V>&gt;" />
    ///    it still owns the objects.
    /// </remarks>
    function GetRange(index, count: Integer): IList<TPair<K,V>>;

    procedure Exchange(index1, index2: Integer);
    procedure Move(currentIndex, newIndex: Integer);

    procedure Reverse; overload;
    procedure Reverse(index, count: Integer); overload;

    procedure Sort; overload;
    procedure Sort(const comparer: IComparer<K>); overload;
    procedure Sort(const comparer: TComparison<K>); overload;
    procedure Sort(const comparer: IComparer<K>; index, count: Integer); overload;
    procedure Sort(const comparer: TComparison<K>; index, count: Integer); overload;

    /// <summary>
    ///   Determines the index of a specific item in the IList&lt;TPair<K,V>&gt;.
    /// </summary>
    /// <param name="item">
    ///   The element to locate in the IList&lt;TPair<K,V>&gt;.
    /// </param>
    /// <returns>
    ///   The index of <i>item</i> if found in the list; otherwise, -1.
    /// </returns>
    /// <remarks>
    ///   If an element occurs multiple times in the list, the IndexOf method
    ///   always returns the first instance found.
    /// </remarks>
    function IndexOf(const Key: K): Integer; overload;
    function IndexOf(const Key: K; index: Integer): Integer; overload;
    function IndexOf(const Key: K; index, count: Integer): Integer; overload;

    function AsList: IList;

    /// <summary>
    ///   Returns the list as read-only list.
    /// </summary>
    /// <remarks>
    ///   This method will not perform a copy but will return the same instance
    ///   as IReadOnlyList&lt;TPair<K,V>&gt;.
    /// </remarks>
    function AsReadOnlyList: IReadOnlyList<TPair<K,V>>;
    procedure TrimExcess;

    property Capacity: Integer read GetCapacity write SetCapacity;
    property Count: Integer read GetCount write SetCount;
    property Items[index: Integer]: TPair<K,V> read GetItem write SetItem; default;
  end;

如果您知道(或可以控制)字典如何存储其项目,您可以将 List 设置为 TArray<Integer> 保存字典使用的存储索引号或 TArray<pointer to TPair<K,V>>.
如果 KV 很小,最好只复制列表。

更好的选择可能是使用 B+Tree 来存储您的项目,在语义上它位于字典和列表之间的某个位置。