不管在c#中還是java中,很多類型都有contains方法。它的原理是什么?
看一個java的例子
http://blog.csdn.net/fwwdn/article/details/6746849
c#中的contains有:
String.Contains |
List(T).Contains |
Enumerable.Contains(TSource) |
Vector.contains |
Queue(T).Contains |
Enumerable.Contains |
Collection.contains |
HashSet(T).Contains |
ICollection(T).Contains |
Array.contains |
IQueryable(T).Contains |
Hashtable.Contains |
ArrayList.contains |
Collection(T).Contains |
Enumerable.Contains(TSource) |
EntityCollection(TEntity).Contains |
Stack(T).Contains |
SortedList.Contains |
PropertyBag.Contains |
等等。。。。。。。。。
看了下msdn,這些contains的原理,大致分三種
1,默認相等比較器
2,equals
3,hashcode
以List<T> 為例,其contains方法的定義為
// System.Collections.Generic.List<T> /// <summary>Determines whether an element is in the <see cref="T:System.Collections.Generic.List`1" />.</summary> /// <returns>true if <paramref name="item" /> is found in the <see cref="T:System.Collections.Generic.List`1" />; otherwise, false.</returns> /// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.List`1" />. The value can be null for reference types.</param> [__DynamicallyInvokable] public bool Contains(T item) { if (item == null) { for (int i = 0; i < this._size; i++) { if (this._items[i] == null) { return true; } } return false; } EqualityComparer<T> @default = EqualityComparer<T>.Default; for (int j = 0; j < this._size; j++) { if (@default.Equals(this._items[j], item)) { return true; } } return false; }
可以看到,使用了默認相等比較器的方法Equals:EqualityComparer,看一下其定義
http://msdn.microsoft.com/zh-cn/library/ms224763(v=vs.110).aspx
public abstract class EqualityComparer<T> : IEqualityComparer, IEqualityComparer<T>
這兩個接口只有兩個方法,Equals和GetHashCode,但是EqualityComparer是一個抽象類,它實現了IEqualityComparer接口的兩個方法且定義為私有方法,卻把IEqualityComparer<T>接口的兩個方法實現為了抽象方法。
那么因為@default.Equals調用的是IEqualityComparer<T>接口的方法,所以我們要知道Equals的實現方式,關鍵是在Default的獲取上,通過它才可以看到Equals的定義,看一下Default的定義
public static EqualityComparer<T> Default { get { EqualityComparer<T> equalityComparer = EqualityComparer<T>.defaultComparer; if (equalityComparer == null) { equalityComparer = EqualityComparer<T>.CreateComparer(); EqualityComparer<T>.defaultComparer = equalityComparer; } return equalityComparer; } }
這里邊defaultComparer的定義:(todo volatile 修飾符的定義)
private static volatile EqualityComparer<T> defaultComparer;
我們暫時當它是null的,那么就會調用私有方法CreateComparer了。
private static EqualityComparer<T> CreateComparer() { RuntimeType runtimeType = (RuntimeType)typeof(T); if (runtimeType == typeof(byte)) { return (EqualityComparer<T>)new ByteEqualityComparer(); } if (typeof(IEquatable<T>).IsAssignableFrom(runtimeType)) { return (EqualityComparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(GenericEqualityComparer<int>), runtimeType); } if (runtimeType.IsGenericType && runtimeType.GetGenericTypeDefinition() == typeof(Nullable<>)) { RuntimeType runtimeType2 = (RuntimeType)runtimeType.GetGenericArguments()[0]; if (typeof(IEquatable<>).MakeGenericType(new Type[] { runtimeType2 }).IsAssignableFrom(runtimeType2)) { return (EqualityComparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(NullableEqualityComparer<int>), runtimeType2); } } if (runtimeType.IsEnum && Enum.GetUnderlyingType(runtimeType) == typeof(int)) { return (EqualityComparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(EnumEqualityComparer<int>), runtimeType); } return new ObjectEqualityComparer<T>(); }
在這里,根據不同的運行時類型,實例化不同的EqualityComparer子類。在此只看最后一個return(todo 其他子類),ObjectEqualityComparer:這是一個internal的類,我們是用不了的
internal class ObjectEqualityComparer<T> : EqualityComparer<T>
然后我們可以找到Equals,這里會有兩個
public override bool Equals(T x, T y) { if (x != null) { return y != null && x.Equals(y); } return y == null; } public override bool Equals(object obj) { ObjectEqualityComparer<T> objectEqualityComparer = obj as ObjectEqualityComparer<T>; return objectEqualityComparer != null; }
我們用到的是第一個(todo,第二個)。我們看到,這里又一次調用了Equals,但是這個Equals很好定位,它就是Object.Equals
如果當前實例是引用類型,Equals(Object) 方法測試引用相等性,並且,對於 Equals(Object) 方法的調用等效於 ReferenceEquals 方法的調用。 引用相等性意味着進行比較的對象變量引用同一個對象。如果當前實例是值類型,Equals(Object) 方法測試值相等性。
派生類通常重寫 Object.Equals(Object) 方法實現值相等性。 此外,類型通常還提供其他的重載到 Equals 方法的強類型,通常通過實現 IEquatable<T> 接口。 當您調用 Equals 方法測試是否相等時,應知道是否當前實例重寫 Object.Equals 並了解如何解決對 Equals 方法的特定調用。 否則,您可以執行與您預期不同的相等測試,因此,方法可能會返回意外的值。
http://msdn.microsoft.com/zh-cn/library/bsc2ak47.aspx
追下去后:
public virtual bool Equals(object obj) { return RuntimeHelpers.Equals(this, obj); }
[SecuritySafeCritical] [MethodImpl(MethodImplOptions.InternalCall)] public new static extern bool Equals(object o1, object o2);
我覺得系統內置的類的Equals方法,都會重寫的。我們關心的是我們自定義的類是怎么使用Equals的。但是這里開始導入外部的方法了,再進一步不知道該怎么辦了(todo 繼續追查,extern修飾符的定義)。
http://blog.csdn.net/llddyy123wq/article/details/5620466
--======================================================================================
這條路走不通,換一個類型,試試HashSet<T>,首先找到它的contains方法
public bool Contains(T item) { if (this.m_buckets != null) { int num = this.InternalGetHashCode(item); for (int i = this.m_buckets[num % this.m_buckets.Length] - 1; i >= 0; i = this.m_slots[i].next) { if (this.m_slots[i].hashCode == num && this.m_comparer.Equals(this.m_slots[i].value, item)) { return true; } } } return false; }
看了下,m_comparer是IEqualityComparer<T>類型的,所以這里的Equals和上邊是一樣的。除了Equals,hashset還要比較hashcode,這是比較特別的地方。