【问题标题】:How to remove empty/nil elements from Array?如何从数组中删除空/零元素?
【发布时间】:2014-07-04 15:07:16
【问题描述】:

如何从数组中删除空元素或带有 nil 指针的元素?欢迎使用通用解决方案。

【问题讨论】:

  • 从数组中删除任何项目的方法相同。到目前为止,您尝试过什么?
  • @Jerry 这实际上有点微妙,尤其是在您需要通用解决方案时。

标签: delphi delphi-xe6


【解决方案1】:

你可以这样写:

type
  TArrayHelper = class
    class function RemoveAll<T>(var Values: TArray<T>; const Value: T); static;
  end;

....

function TArrayHelper.RemoveAll<T>(var Values: TArray<T>; const Value: T);
var
  Index, Count: Integer;
  DefaultComparer: IEqualityComparer<T>;
begin
  // obtain an equality comparer for our type T
  DefaultComparer := TEqualityComparer<T>.Default;

  // loop over the the array, only retaining non-matching values
  Count := 0;
  for Index := 0 to high(Values) do begin
    if not DefaultComparer.Equals(Values[Index], Value) then begin
      Values[Count] := Values[Index];
      inc(Count);
    end;
  end;

  // re-size the array
  SetLength(Values, Count);
end;

假设你有一个指针数组:

var
  arr: TArray<Pointer>;

然后你会像这样删除nil 元素:

TArrayHelper.RemoveAll<Pointer>(arr, nil);

此代码采用简单的方法并始终使用默认比较器。对于更复杂的类型,这是不好的。例如,一些记录需要自定义比较器。您需要提供一个比较器来支持它。


上面的实现尽可能简单。就性能而言,在找不到匹配值或找到匹配值很少的可能常见场景中,这很可能是浪费的。那是因为上面的版本无条件分配,即使两个索引相同。

相反,如果存在性能问题,您可以通过逐步遍历数组直到第一个匹配项来优化代码。然后才开始移动值。

function TArrayHelper.RemoveAll<T>(var Values: TArray<T>; const Value: T);
var
  Index, Count: Integer;
  DefaultComparer: IEqualityComparer<T>;
begin
  // obtain an equality comparer for our type T
  DefaultComparer := TEqualityComparer<T>.Default;

  // step through the array until we find a match, or reach the end
  Count := 0;
  while (Count<=high(Values)) 
  and not DefaultComparer.Equals(Values[Count], Value) do begin
    inc(Count);
  end;
  // Count is either the index of the first match or one off the end

  // loop over the rest of the array copying non-matching values to the next slot
  for Index := Count to high(Values) do begin
    if not DefaultComparer.Equals(Values[Index], Value) then begin
      Values[Count] := Values[Index];
      inc(Count);
    end;
  end;

  // re-size the array
  SetLength(Values, Count);
end;

正如您所见,这要分析起来要困难得多。如果原始版本是瓶颈,您只会考虑这样做。

【讨论】:

  • FWIW,在我自己的代码库中,我有一个名为 TArray 的类,用于此类静态类方法。我从Generics.Collections.TArray 派生了该类,因此我不需要介绍另一个名称。
  • 这非常有效,因为只有一个SetLength 调用。
  • 我并不感到惊讶..... ;-) 当i=Count 时,您可能希望优化掉Values[Count] := Values[i]; 这可能会更好。很难确定。分支可能不好。
  • 我是用真实代码写的。我根本没有测试过它。这是相当微妙的。我可能听错了。请不要指望我犯错!
  • 我认为很难击败第二个版本,至少从算法的立场来看是这样。当然,编写的代码可能会被破坏,因为我什至没有编译过任何代码!
猜你喜欢
  • 2023-03-10
  • 2011-08-18
  • 2019-11-28
  • 2019-08-23
  • 2018-08-06
  • 2013-01-26
  • 1970-01-01
  • 2022-11-16
相关资源
最近更新 更多