Nit*_*esh 76 c# list duplicates
我想从列表中删除重复项,而不更改列表中唯一元素的顺序.
Jon Skeet和其他人建议使用以下内容
list = list.Distinct().ToList();
Run Code Online (Sandbox Code Playgroud)
是否保证独特元素的顺序与以前相同?如果是,请提供一个确认的参考,因为我在文档中找不到任何内容.
Jon*_*eet 67
它不能保证,但它是最明显的实现.很难以流式方式实现(即,它尽可能快地返回结果,尽可能少地读取)而不按顺序返回它们.
您可能想阅读关于Distinct()的Edulinq实现的博客文章.
请注意,即使LINQ to Objects(我个人觉得它应该是这样)保证这对于其他LINQ提供程序(例如LINQ to SQL)也没有任何意义.
在LINQ to Objects中提供的保证级别有时会有点不一致,IMO.记录了一些优化,其他则没有.哎呀,有些文件是错误的.
Ser*_*kiy 26
是的,按原始列表中第一次出现的顺序排列.它保证适用于.Net Framework 3.5
我用Reflector进行了一些调查.在反汇编System.Core.dll,版本= 3.5.0.0后,您可以看到Distinct()是一个扩展方法,如下所示:
public static class Emunmerable
{
public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source)
{
if (source == null)
throw new ArgumentNullException("source");
return DistinctIterator<TSource>(source, null);
}
}
Run Code Online (Sandbox Code Playgroud)
所以,这里有趣的是DistinctIterator,它实现了IEnumerable和IEnumerator.这是IEnumerator的简化(goto和lables删除)实现:
private sealed class DistinctIterator<TSource> : IEnumerable<TSource>, IEnumerable, IEnumerator<TSource>, IEnumerator, IDisposable
{
private bool _enumeratingStarted;
private IEnumerator<TSource> _sourceListEnumerator;
public IEnumerable<TSource> _source;
private HashSet<TSource> _hashSet;
private TSource _current;
private bool MoveNext()
{
if (!_enumeratingStarted)
{
_sourceListEnumerator = _source.GetEnumerator();
_hashSet = new HashSet<TSource>();
_enumeratingStarted = true;
}
while(_sourceListEnumerator.MoveNext())
{
TSource element = _sourceListEnumerator.Current;
if (!_hashSet.Add(element))
continue;
_current = element;
return true;
}
return false;
}
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
TSource IEnumerator<TSource>.Current
{
get { return _current; }
}
object IEnumerator.Current
{
get { return _current; }
}
}
Run Code Online (Sandbox Code Playgroud)
正如你所看到的 - 枚举顺序由source enumerable提供(list,我们在其上调用Distinct).Hashset仅用于确定我们是否已经返回此类元素.如果没有,我们将返回它,否则 - 继续枚举源.
因此,可以保证,Distinct()将返回完全按相同顺序排列的元素,这些元素由应用了Distinct的集合提供.
是的,Enumerable.Distinct保留顺序。假设该方法是“懒惰的”,“一旦看到它们就会产生不同的值”,它会自动执行。想一想。
的.NET参考源确认。它返回一个子序列,每个子类中的第一个元素。
foreach (TSource element in source)
if (set.Add(element)) yield return element;
Run Code Online (Sandbox Code Playgroud)
在.NET核心实现是类似的。
令人沮丧的是,Enumerable.Distinct的文档在这一点上感到困惑:
结果序列是无序的。
我只能想象它们的意思是“结果序列未排序”。您可以通过预排序然后将每个元素与前一个元素进行比较来实现Distinct,但这并不像上面定义的那样懒惰。
有点晚了,但没有人真正发布了完成此 IMO 的最佳完整代码,所以让我提供这个(这与 .NET Framework 使用 Distinct() 所做的基本上相同)*:
public static IEnumerable<T> DistinctOrdered<T>(this IEnumerable<T> items)
{
HashSet<T> returnedItems = new HashSet<T>();
foreach (var item in items)
{
if (returnedItems.Add(item))
yield return item;
}
}
Run Code Online (Sandbox Code Playgroud)
这保证了原始订单,而不依赖于未记录或假设的行为。我还相信这比使用多个 LINQ 方法更有效,尽管我愿意在这里纠正。
(*) .NET Framework 源使用内部Set类,它看起来与HashSet.
| 归档时间: |
|
| 查看次数: |
13166 次 |
| 最近记录: |