zoukankan      html  css  js  c++  java
  • contains 方法

    不管在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,这是比较特别的地方。

  • 相关阅读:
    Linux unalias命令 取消别名
    linux cp 拷贝文件或目录
    POJ 1850
    POJ 1844
    POJ 1852
    POJ 1837
    POJ 1833
    POJ 1804
    POJ 1789
    POJ 1781
  • 原文地址:https://www.cnblogs.com/yyjj/p/4086791.html
Copyright © 2011-2022 走看看