List的性能问题

时间:2014-08-08 07:47:18

标签: c# performance list for-loop

我的数据库中有1000万条记录,我需要随机更新所有20行值 因此,对于每个随机的5000万条记录,需要更新1个值 因此,我想到了生成一个包含1000万个数字的列表,并从该列表中随机选择了5000万条记录,并从该列表中删除了5000万条记录,依此类推。

我的代码:

列表创建:

List<long> LstMainList = new List<long>();
for (int i = 1; i <= 999999999; i++)
{
   LstMainList.Add(i);
}

新空列表: List<TableData> Table1 = new List<TableData>();

选择随机数并将其添加到新列表并从MainList中删除包含1000万个项目的项目。

Random rand = new Random();

for (int a = 0; a < 50000000; a++)
{
     int lstindex = rand.Next(LstMainList.Count);

     Int64 lstData = LstMainList[lstindex];

     Table1.Add(new TableData { MESSAGE_ID = lstData });

     LstMainList.RemoveAt(lstindex);

     if (a % 100000 == 0)
     {
         if (previousThread != null)
         {
              previousThread.Join();
         }

          List<TableData> copyList = Table1.ToList();

          previousThread = new Thread(() => BulkCopyList(copyList, "PLAN_TABLE_1"));
          previousThread.Start();

          Table1.Clear();
       }
}

现在,我的问题是:在LstMainList.RemoveAt(lstindex);行,需要很长时间才能从MainList中删除索引,因为它包含1000万条记录。

有没有办法以简单的方式从List中删除记录?或任何其他方式使这个简单?

3 个答案:

答案 0 :(得分:4)

首先 - 使用数组代替列表而不是列表(特别是没有初始化容量)

int idsCount = 100000000;
long[] ids = new long[idsCount];

for(long i = 1; i < idsCount; i++)
    ids[i] = i;

使用Fisher–Yates shuffle在数组中随机播放id

Random rnd = new Random();
int n = idsCount;
while(n > 1)
{
    int k = rnd.Next(n);
    n--;
    long temp = ids[n];
    ids[n] = ids[k];
    ids[k] = temp;
}

使用混乱ID,您无需修改​​ID列表。在随机位置移除物品是非常昂贵的操作。如果删除位置0处的项目,则应将整个列表复制到新数组。现在你可以迭代ids数组。

或者您可以使用morelinq Batch创建批量的TableData并批量处理它们:

int size = 100000;
foreach(var batch in ids.Batch(size, id => new TableData { MESSAGE_ID = id }))
{
   var copyList = batch.ToList();
   // ...
}

更新:因此您需要不同大小的批次,您可以使用以下扩展方法从数组中获取项目范围:

public static IEnumerable<T> GetRange<T>(
     this T[] array, int startIndex, int count)
{
    for (int i = startIndex; i < startIndex + count; i++)
        yield return array[i];
}

因此,从索引20000开始获取5000 TableData将如下所示:

var copyList = ids.GetRange(20000, 5000)
                  .Select(id => new TableData { MESSAGE_ID = id })
                  .ToList();

当然,更有效的方法是迭代ids数组,并使用预初始化容量将项目添加到列表中:

int size = 5000;
int startIndex = 20000;
List<TableData> copyList = new List<TableData>(size);
for (int i = startIndex; i < startIndex + size; i++)
    copyList.Add(new TableData { MESSAGE_ID = ids[i] });

更进一步,我会将TableData对象创建移动到执行批量复制的线程。并且只是传递了它应该使用的id序列。

答案 1 :(得分:0)

首先,这是some advice from Microsoft about selecting rows randonly from a large table

其次,如果没用,请继续阅读...

如果您知道要随机选择的项目数,以及您想要随机选择的序列中的项目数,则可以使用O(N)解决方案。

在下面的示例中,方法RandomlySelectedItems<T>()提供了一系列随机选择的项目。

这是代码。 (重申一下,如果您事先知道要选择的项目数量,则只能使用此项目):

using System;
using System.Collections.Generic;
using System.Linq;

namespace Demo
{
    internal static class Program
    {
        static void Main(string[] args)
        {
            int numberOfValuesToSelectFrom = 10000000;
            int numberOfValuesToSelect = 20;
            var valuesToSelectFrom = Enumerable.Range(1, numberOfValuesToSelectFrom);

            var selectedValues = RandomlySelectedItems
            (
                valuesToSelectFrom, 
                numberOfValuesToSelect, 
                numberOfValuesToSelectFrom, 
                new Random()
           );

            foreach (int value in selectedValues)
                Console.WriteLine(value);
        }

        /// <summary>Randomly selects items from a sequence.</summary>
        /// <typeparam name="T">The type of the items in the sequence.</typeparam>
        /// <param name="sequence">The sequence from which to randomly select items.</param>
        /// <param name="count">The number of items to randomly select from the sequence.</param>
        /// <param name="sequenceLength">The number of items in the sequence among which to randomly select.</param>
        /// <param name="rng">The random number generator to use.</param>
        /// <returns>A sequence of randomly selected items.</returns>
        /// <remarks>This is an O(N) algorithm (N is the sequence length).</remarks>

        public static IEnumerable<T> RandomlySelectedItems<T>(IEnumerable<T> sequence, int count, int sequenceLength, Random rng)
        {
            if (sequence == null)
                throw new ArgumentNullException("sequence");

            if (count < 0 || count > sequenceLength)
                throw new ArgumentOutOfRangeException("count", count, "count must be between 0 and sequenceLength");

            if (rng == null)
                throw new ArgumentNullException("rng");

            int available = sequenceLength;
            int remaining = count;
            var iterator  = sequence.GetEnumerator();

            for (int current = 0; current < sequenceLength; ++current)
            {
                iterator.MoveNext();

                if (rng.NextDouble() < remaining/(double)available)
                {
                    yield return iterator.Current;
                    --remaining;
                }

                --available;
            }
        }
    }
}

答案 2 :(得分:0)

一种选择是不尝试生成真正的甚至是伪随机数,而是使用对于不经意的观察者而言显然是随机的序列。这可以在很多情况下工作,但是如果需要随机选择项目以防止攻击者预测下一个值,它将无法工作。好处是你不需要跟踪内存中所有生成的值来改变它们。

首先,选择两个随机素数(a,b)小于行数(r),使a * b&gt; r和a不分r。映射f(x)= a * x + b mod r保证在环Z [r]中是一对一的。我们将使用该事实生成一个序列,其中每个值在0到r - 1之间是唯一的。

让我们选择两个随机素数,比如a = 11268619和b = 4064861.然后你可以生成&#34;随机序列&#34;数字范围为0到1e9-1:

private static IEnumerable<int> GenerateSequence()
{
    const int max = 1000000000;
    const long a = 11268619, b = 4064861;

    for(int i = 0; i < max; i++)
    {
        int c = (int)((a * i + b) % max);
        yield return c;
    }
}