使用TDictionary"for ... in"

Tia*_*ago 1 delphi dictionary

我正在使用Delphi Berlin 10.1(最新的更新),我在我的应用程序中遇到了一些特殊值的TDictionary问题."for..in"无法正确循环.

下面是一个示例代码,其中"for ... in"不循环遍历所有值,另一个示例表示它.

在第一种情况下,"for ... in"循环只执行两步,而在第二种情况下,它执行所有步骤.

procedure TForm1.btn1Click(Sender: TObject);
var
  tmpPar: TPair<Integer, Integer>;
  tmpDictionary: TDictionary<Integer, Integer>;
begin
  // NOT WORKING
  tmpDictionary := TDictionary<Integer, Integer>.Create;
  try
      tmpDictionary.Add(631, 40832);
      tmpDictionary.Add(1312, 40837);
      tmpDictionary.Add(5947, 40842);

      for tmpPar in tmpDictionary do
      begin
          tmpDictionary.Remove(tmpPar.Key);
      end;
  finally
      tmpDictionary.Free;
  end;

  // WORKING
  tmpDictionary := TDictionary<Integer, Integer>.Create;
  try
      tmpDictionary.Add(123, 5432);
      tmpDictionary.Add(453, 23);
      tmpDictionary.Add(76, 2334);

      for tmpPar in tmpDictionary do
      begin
          tmpDictionary.Remove(tmpPar.Key);
      end;
  finally
      tmpDictionary.Free;
  end;
end;
Run Code Online (Sandbox Code Playgroud)

第一种情况有什么问题吗?

提前致谢!

J..*_*... 12

你运作的例子很好用 - 运气不应该期望这个结构表现得很好.如果您单步执行示例,您会看到第一个案例在删除时调用列表重新排序,但第二个示例没有.

要查看发生了什么,如果您检查从字典中删除项目的代码:

function TDictionary<TKey,TValue>.DoRemove(const Key: TKey; HashCode: Integer;
  Notification: TCollectionNotification): TValue;
var
  gap, index, hc, bucket: Integer;
  LKey: TKey;
begin
  index := GetBucketIndex(Key, HashCode);
  if index < 0 then
    Exit(Default(TValue));

  // Removing item from linear probe hash table is moderately
  // tricky. We need to fill in gaps, which will involve moving items
  // which may not even hash to the same location.
  // Knuth covers it well enough in Vol III. 6.4.; but beware, Algorithm R
  // (2nd ed) has a bug: step R4 should go to step R1, not R2 (already errata'd).
  // My version does linear probing forward, not backward, however.

  // gap refers to the hole that needs filling-in by shifting items down.
  // index searches for items that have been probed out of their slot,
  // but being careful not to move items if their bucket is between
  // our gap and our index (so that they'd be moved before their bucket).
  // We move the item at index into the gap, whereupon the new gap is
  // at the index. If the index hits a hole, then we're done.

  // If our load factor was exactly 1, we'll need to hit this hole
  // in order to terminate. Shouldn't normally be necessary, though.
  {...   etc   ...}
Run Code Online (Sandbox Code Playgroud)

您会看到实现了一种算法,该算法决定何时以及如何在删除项目时对基础列表进行重新排序(这是为了尝试优化已分配内存块中的间隙位置以优化未来的插入).枚举只是通过基础列表中的索引移动,因此一旦从列表中删除项目,枚举器就不再有效,因为它只会将您移动到基础列表中的下一个索引,该索引已经更改.

对于普通列表,您通常会在删除时反向迭代.但是,对于字典,您必须首先构建要在第一个枚举过程中删除的键列表,然后枚举列表以将其从字典中删除.