List 如何从列表中删除所有重复项?

List 如何从列表中删除所有重复项?,list,delphi,unique,delphi-xe7,spring4d,List,Delphi,Unique,Delphi Xe7,Spring4d,考虑这个测试应用程序: function RemoveDuplicates(const Input: IEnumerable<Integer>): IEnumerable<Integer>; begin // How to implement this function? end; var Enumerable: IEnumerable<Integer>; UniqueEnumerable: IEnumerable<Integer>;

考虑这个测试应用程序:

function RemoveDuplicates(const Input: IEnumerable<Integer>): IEnumerable<Integer>;
begin
  // How to implement this function?
end;

var
  Enumerable: IEnumerable<Integer>;
  UniqueEnumerable: IEnumerable<Integer>;
begin
  Enumerable := TCollections.CreateList<Integer>([1, 1, 2, 3, 3, 3, 4]);
  UniqueEnumerable := RemoveDuplicates(Enumerable);
  UniqueEnumerable.ForEach(
    procedure(const I: Integer)
    begin
      WriteLn(I);
    end);
  ReadLn;
end.
函数移除副本(常量输入:IEnumerable):IEnumerable;
开始
//如何实现这个功能?
结束;
变量
可枚举:IEnumerable;
不可数:IEnumerable;
开始
可枚举:=TCollections.CreateList([1,1,2,3,3,4]);
Uniquenumerable:=移除的副本(可枚举);
无法计算的ForEach(
过程(常数I:整数)
开始
书面(I);
(完),;
ReadLn;
结束。

如何使用中间列表实现
RemoveDuplicates
功能(在Haskell中称为
nub

function RemoveDuplicates(const Input: IEnumerable<Integer>): IEnumerable<Integer>;
var
  List: IList<Integer>;
begin
  List := TCollections.CreateList<Integer>;
  Input.ForEach(
    procedure(const I: Integer)
    begin
      if not List.Contains(I) then
        List.Add(I);
    end);
  Result := List;
end;
函数移除副本(常量输入:IEnumerable):IEnumerable;
变量
名单:IList;
开始
列表:=TCollections.CreateList;
Input.ForEach(
过程(常数I:整数)
开始
如果列表中没有包含(I),则
列表.添加(I);
(完),;
结果:=列表;
结束;

这显然不是最好的解决方案,请参阅其他答案以获得更好的选择。

出于性能原因,我建议使用排序列表字典

function RemoveDuplicates(const Input: IEnumerable<Integer>): IEnumerable<Integer>;
var
  Dictionary: IDictionary<integer, integer>;
  Item: integer;
begin
  Dictionary := TCollections.CreateDictionary<integer,integer>;
  for Item in Input do
    Dictionary.AddOrSetValue(Item, 0);     

  Result := Dictionary.Keys;
end;
函数移除副本(常量输入:IEnumerable):IEnumerable;
变量
词典:词典;
项目:整数;
开始
Dictionary:=TCollections.CreateDictionary;
对于输入do中的项目
Dictionary.AddOrSetValue(项,0);
结果:=Dictionary.Keys;
结束;

Jens的解决方案可以工作,但运行时间相当慢,为O(n2)

如果您有一个很长的列表,一个更好的选择是
-对列表进行排序
-将每个项目与其后续项目进行比较

快速排序的运行时间为O(n logn),搜索总运行时间为O(n logn)

请参阅以下代码(现在无法访问Delphi)

函数移除副本(常量输入:IEnumerable):IEnumerable;
变量
名单:IList;
i:整数;
开始
列表:=TCollections.CreateList;
列表。分配(输入)//将输入列表复制到输出。
列表.排序;
对于i:=List.Count-1 down到1 do begin
如果List[i]=List[i-1],则List.delete(i);
//如果比较器等于(列表[i],列表[i-1]),那么。。。。
结束;{for i}
结束;
问题
这种方法的问题在于输出(可能)与输入的顺序不同。这可能是问题,也可能不是问题

好处(或词典为什么糟糕)
如果排序是一种廉价的操作,这将是最快的方法。
使用字典会带来很高的哈希固定成本。
即使散列操作是O(1),对于大的键,它可能会变得非常昂贵,因为散列将始终处理整个键,而排序比较将在检测到差异后立即停止。 进一步注意,散列操作比简单的比较(大约慢30到100倍)要昂贵得多


只有当列表很大时,听写输入的渐进运行时间才会更好。

使用已有的内容:

uses
  Spring.Collections,
  Spring.collections.Extensions;

function RemoveDuplicates(const Input: IEnumerable<Integer>): IEnumerable<Integer>;
begin
  Result := TDistinctIterator<Integer>.Create(Input, nil);
end;
使用
春季系列,
Spring.collections.Extensions;
函数移除副本(常量输入:IEnumerable):IEnumerable;
开始
结果:=tdistiniterator.Create(输入,无);
结束;
这支持延迟求值(意味着在处理结果可枚举项之前不处理输入)。它在内部使用hashset(当前实现为Dictionary)来跟踪已经找到的项(这发生在枚举器内部)

为什么这很重要?因为如果
Input
涉及到其他代价高昂的操作,则任何执行完整枚举的操作都可能会导致不必要的性能影响,这可能远远超过其他删除重复项的方法(如将其放入列表并进行排序)的任何好处。此外,IEnumerable也不能保证是有限的


如果在调用此函数和枚举结果之间更改了
输入
,则更改会影响枚举的结果,而如果不支持延迟求值,则情况并非如此。如果多次枚举,每次的结果可能不同(即最新)。

字典是O(n)。你的方法是O(n logn)。@DavidHeffernan对不起,我不明白重点。字典查找复杂度为O(n)的重复项如何进行?因为字典查找是
O(1)
,并且有
n
项要处理。请注意,字典的O(1)查找时间具有较高的常数,因此它的性能不如您想象的那么好!这是最慢的方法,即使对于较小的列表,运行时间也很糟糕。当然,这也是最直接的方法。我主要是把它作为参考发布的。你在整数上使用什么哈希函数?或者,换句话说,在这种情况下,你在这里写的很多东西都是完全错误的。您可能有一点认为哈希是昂贵的,但这里的情况并非如此。不管它有多昂贵,对于相当大的数据,早在RAM满之前,您就可以期待哈希运算获胜。@david我更倾向于假设integer只是这里的一个例子。如果使用默认的bobjenkins lookup3散列,即使对于整数,也会非常慢。当然有许多常用类型可以有效地进行散列。所以,“为什么字典这么烂”是一个过于戏剧化的说法,也许是误导性的说法。在我看来。整数的默认哈希不是整数本身吗?确实如此。嗯,不,这不是真的。我想知道为什么不可以。没有system.generics.defaults会通过bobjenkins的lookup3哈希推送所有内容。因此,我断言速度缓慢。如果只使用部分整数(例如32个中的16个),那么使用lookup3是有意义的。FWIW I最近通过哈希查找解决了重复数据消除性能问题。我甚至不确定我是否能与分拣相比较。通常情况下,当数据小到足以进行排序时
uses
  Spring.Collections,
  Spring.collections.Extensions;

function RemoveDuplicates(const Input: IEnumerable<Integer>): IEnumerable<Integer>;
begin
  Result := TDistinctIterator<Integer>.Create(Input, nil);
end;