首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用TDictionary "for...in“

使用TDictionary "for...in“
EN

Stack Overflow用户
提问于 2017-10-17 19:42:33
回答 2查看 3.5K关注 0票数 1

我正在使用DelphiBerlin10.1(最新的更新),我在我的应用程序中遇到了一些特殊值的TDictionary问题。"for..in“不能正确循环。

下面有一个示例代码,其中"for...in“不遍历所有值,还有另一个示例。

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

代码语言:javascript
复制
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;

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

提前感谢!

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-10-18 12:51:54

J..。给出解释。最简单的解决办法如下:

代码语言:javascript
复制
var
  tmpKey: Integer;                                //!!!amended
  tmpDictionary: TDictionary<Integer, Integer>;
begin
  // NOW WORKING
  tmpDictionary := TDictionary<Integer, Integer>.Create;
  try
      tmpDictionary.Add(631, 40832);
      tmpDictionary.Add(1312, 40837);
      tmpDictionary.Add(5947, 40842);

      for tmpKey in tmpDictionary.Keys.ToArray do //!!!amended
      begin
          tmpDictionary.Remove(tmpKey);           //!!!amended
      end;
  finally
      tmpDictionary.Free;
  end;
end;

基本上,调用Keys.ToArray会为您提供一个新的密钥副本,它不会在您删除项时从其脚下被删除。

票数 3
EN

Stack Overflow用户

发布于 2017-10-17 20:25:17

你的例子仅仅是靠运气起作用的--不应该期望这个构造会表现得很好。如果您步进您的示例,您将看到第一种情况在删除时调用列表重新排序,而第二种情况则不调用。

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

代码语言:javascript
复制
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   ...}

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

对于普通列表,通常会在删除时反向迭代。但是,在字典的情况下,必须首先在第一次枚举传递时生成要删除的键列表,然后枚举该列表以从字典中删除它们。

票数 13
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46797980

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档