C#Distinct()方法是否保持序列的原始顺序不变?

我想删除列表中的重复项,而不改变列表中唯一元素的顺序。

Jon Skeet和其他人build议使用以下内容

list = list.Distinct().ToList(); 

从列表中删除重复项C#

删除C#中List <T>的重复项

是否保证独特元素的顺序与以前一样? 如果是,请给出一个参考证实这一点,因为我没有find任何文件上的东西。

这不是保证,但这是最明显的实施。 这将很难以stream式方式实现(即,它会尽快返回结果,尽可能less地阅读), 而不会按顺序返回。

你可能想阅读我的博客文章Edulinq实施Distinct() 。

请注意,即使这是保证LINQ to Objects(我个人认为它应该是这样)对其他LINQ提供程序(如LINQ to SQL)没有任何意义。

有时,在LINQ to Objects中提供的保证水平有点不一致,IMO。 一些优化被logging,其他优化没有。 哎呀,有些文档是错误的

是的,按照原始列表中第一次出现的顺序。 它保证 .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); } } 

所以,这里有趣的是DistinctIterator,它实现了IEnumerable和IEnumerator。 这里是简化的(goto和lables删除)这个IEnumerator的实现:

 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; } } } 

正如你所看到的那样 – 枚举按顺序由源enumerable提供(列表,我们在其中调用Distinct)。 Hashset仅用于确定我们是否已经返回了这个元素。 如果没有,我们正在返回它,否则 – 继续枚举源。

因此,Distinct()将确保以完全相同的顺序返回元素,这些元素由Distinct应用于的集合提供。

根据文件序列是无序的。

默认情况下,使用Distinct linq运算符时使用Equals方法,但您可以使用自己的IEqualityComparer<T>对象来指定何时两个对象与实现GetHashCodeEquals方法的自定义逻辑相等。 请记住:

GetHashCode不应该使用大量的cpu比较(例如,只使用一些明显的基本检查),并且如果两个对象确实不同(如果返回不同的哈希码)或者可能是相同的(相同的哈希码),则它用作第一个状态。 在这种最新的情况下,当两个对象具有相同的散列码时,框架将逐步检查使用Equals方法作为关于给定对象的相等性的最终决定。

MyTypeMyTypeEqualityComparerMyTypeEqualityComparer代码不确保顺序维护其顺序后:

 var cmp = new MyTypeEqualityComparer(); var lst = new List<MyType>(); // add some to lst var q = lst.Distinct(cmp); 

在下面的sci库中,我实现了一个扩展方法来确保Vector3D集在使用特定的扩展方法时保持顺序DistinctKeepOrder

相关的代码如下:

 /// <summary> /// support class for DistinctKeepOrder extension /// </summary> public class Vector3DWithOrder { public int Order { get; private set; } public Vector3D Vector { get; private set; } public Vector3DWithOrder(Vector3D v, int order) { Vector = v; Order = order; } } public class Vector3DWithOrderEqualityComparer : IEqualityComparer<Vector3DWithOrder> { Vector3DEqualityComparer cmp; public Vector3DWithOrderEqualityComparer(Vector3DEqualityComparer _cmp) { cmp = _cmp; } public bool Equals(Vector3DWithOrder x, Vector3DWithOrder y) { return cmp.Equals(x.Vector, y.Vector); } public int GetHashCode(Vector3DWithOrder obj) { return cmp.GetHashCode(obj.Vector); } } 

简而言之, Vector3DWithOrder封装了types和顺序整数,而Vector3DWithOrderEqualityComparer封装了原始types比较器。

这是帮助确保订单维护的方法

 /// <summary> /// retrieve distinct of given vector set ensuring to maintain given order /// </summary> public static IEnumerable<Vector3D> DistinctKeepOrder(this IEnumerable<Vector3D> vectors, Vector3DEqualityComparer cmp) { var ocmp = new Vector3DWithOrderEqualityComparer(cmp); return vectors .Select((w, i) => new Vector3DWithOrder(w, i)) .Distinct(ocmp) .OrderBy(w => w.Order) .Select(w => w.Vector); } 

注意 :进一步的研究可以允许find更一般的(使用接口)和优化的方式(不封装对象)。