List和hashtable之查找效率

假设这样的一个场景,有两个List<string>,分别是listA和listB,其中均有超过1w+的数据量,现在要求找出在B中但是不在A中的数据集合,

于是我们写了以下代码

View Code
listB.ForEach(re =>
             {
                 if (!listA.Contains(re))
                 {
                     //.......
                 }
             });

这样写的话,是能够达到我们的需求的,但是很不幸,时间也会消耗很多。

我们看下List.Contains的源代码实现

View Code
[__DynamicallyInvokable]
 public bool Contains(T item)
 {
     if (item == null)
     {
         for (int j = 0; j < this._size; j++)
         {
             if (this._items[j] == null)
             {
                 return true;
             }
         }
         return false;
     }
     EqualityComparer<T> comparer = EqualityComparer<T>.Default;
     for (int i = 0; i < this._size; i++)
     {
         if (comparer.Equals(this._items[i], item))
         {
             return true;
         }
     }
     return false;
 }

这是因为List.Contains 本质是通过循环查找出该条数据,每一次的调用都会重头循环,所以效率很低。显然,这是不可取的。

于是,我们可以利用Hashtable的键值对改善这种情况。

代码如下:

View Code
listA.ForEach(re =>
             {
                 table.Add(re, re);
             });
             listB.ForEach(re =>
                 {
                     if (table.ContainsKey(re))
                     {
                         //......
                     }
                 });

虽然,多了一个循环,但是总时间确实减少了很多。

我们可以看下Hashtable.ContainsKey源代码:

View Code
public virtual bool ContainsKey(object key)
 {
     uint num;
     uint num2;
     Hashtable.bucket bucket;
     if (key == null)
     {
         throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
     }
     Hashtable.bucket[] buckets = this.buckets;
     uint num3 = this.InitHash(key, buckets.Length, out num, out num2);
     int num4 = 0;
     int index = (int) (num % buckets.Length);
     do
     {
         bucket = buckets[index];
         if (bucket.key == null)
         {
             return false;
         }
         if (((bucket.hash_coll & 0x7fffffff) == num3) && this.KeyEquals(bucket.key, key))
         {
             return true;
         }
         index = (int) ((index + num2) % ((ulong) buckets.Length));
     }
     while ((bucket.hash_coll < 0) && (++num4 < buckets.Length));
     return false;
 }

可见,哈希表的数据查找效率不是盖出来的。

我们可以通过下面的代码验证:

View Code
static void Main(string[] args)
         {
             List<string> listA = new List<string>();
             List<string> listB = new List<string>();
             for (int i = 0; i < 9999; i++)
             {
                 listA.Add(i.ToString());
                 listB.Add((i + 1).ToString());
             }
             Stopwatch watch = new Stopwatch();
             watch.Start();
             listB.ForEach(re =>
             {
                 if (!listA.Contains(re))
                 {
                     //.......
                 }
             });
             watch.Stop();
             Console.WriteLine("List.Contains need {0}", watch.ElapsedTicks);
             Hashtable table = new Hashtable();
             watch.Restart();
             listA.ForEach(re =>
             {
                 table.Add(re, re);
             });
             listB.ForEach(re =>
                 {
                     if (table.ContainsKey(re))
                     {
                         //......
                     }
                 });
             watch.Stop();
             Console.WriteLine("Hashtable.Contains need {0}", watch.ElapsedTicks);
             Console.ReadKey(true);
         }

执行的结果如下:

List.Contains need 1920001 Hashtable.Contains need 8493 可以看到,效率提升了很多,当数据量越大,效果越明显。

注:本帖只是为了说明可以使用Hashtable改善List的查找性能,不做其他用处

原文地址:https://www.cnblogs.com/binyao/p/3051438.html