将二维数组转换为List(一维)的快速方法

时间:2011-02-27 09:26:51

标签: c# arrays list type-conversion

我有一个二维数组,我需要将它转换为List(同一个对象)。我不希望使用forforeach循环执行此操作,该循环将获取每个元素并将其添加到列表中。还有其他方法吗?

3 个答案:

答案 0 :(得分:54)

嗯,你可以使用“blit”类型的副本,虽然它确实意味着制作一个额外的副本:(

double[] tmp = new double[array.GetLength(0) * array.GetLength(1)];    
Buffer.BlockCopy(array, 0, tmp, 0, tmp.Length * sizeof(double));
List<double> list = new List<double>(tmp);

如果您对单维数组感到满意,请忽略最后一行:)

Buffer.BlockCopy是作为本机方法实现的,我希望在验证后使用非常有效的复制。接受List<T> constructor的{​​{1}}针对其实施IEnumerable<T>的情况进行了优化,如IList<T>所做的那样。它将创建一个正确大小的后备数组,并要求它将自身复制到该数组中。希望这也会使用double[]或类似的东西。

以下是三种方法的快速基准(for循环,Buffer.BlockCopy和Buffer.BlockCopy):

Cast<double>().ToList()

结果(以毫秒为单位的时间);

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

class Program
{
    static void Main(string[] args)
    {
        double[,] source = new double[1000, 1000];
        int iterations = 1000;

        Stopwatch sw = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            UsingCast(source);
        }
        sw.Stop();
        Console.WriteLine("LINQ: {0}", sw.ElapsedMilliseconds);

        GC.Collect();
        GC.WaitForPendingFinalizers();

        sw = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            UsingForLoop(source);
        }
        sw.Stop();
        Console.WriteLine("For loop: {0}", sw.ElapsedMilliseconds);

        GC.Collect();
        GC.WaitForPendingFinalizers();

        sw = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            UsingBlockCopy(source);
        }
        sw.Stop();
        Console.WriteLine("Block copy: {0}", sw.ElapsedMilliseconds);
    }


    static List<double> UsingCast(double[,] array)
    {
        return array.Cast<double>().ToList();
    }

    static List<double> UsingForLoop(double[,] array)
    {
        int width = array.GetLength(0);
        int height = array.GetLength(1);
        List<double> ret = new List<double>(width * height);
        for (int i = 0; i < width; i++)
        {
            for (int j = 0; j < height; j++)
            {
                ret.Add(array[i, j]);
            }
        }
        return ret;
    }

    static List<double> UsingBlockCopy(double[,] array)
    {
        double[] tmp = new double[array.GetLength(0) * array.GetLength(1)];    
        Buffer.BlockCopy(array, 0, tmp, 0, tmp.Length * sizeof(double));
        List<double> list = new List<double>(tmp);
        return list;
    }
}

编辑:在每次迭代时更改了for循环以调用LINQ: 253463 For loop: 9563 Block copy: 8697 ,for循环和块复制大约需要同时进行。

答案 1 :(得分:29)

double[, ]转换为List<double>?如果你正在寻找单行,那么

double[,] d = new double[,]
{
    {1.0, 2.0},
    {11.0, 22.0},
    {111.0, 222.0},
    {1111.0, 2222.0},
    {11111.0, 22222.0}
};
List<double> lst = d.Cast<double>().ToList()

<小时/> 但是,如果您正在寻找有效的方法,我宁愿您不要使用此代码。
请遵循下面提到的两个答案中的任何一个。两者都在实施更好的技术。

答案 2 :(得分:11)

for循环是最快的方法。

您可以使用LINQ进行此操作,但速度会慢一些。虽然你自己不写循环,但仍然有一个循环。

  • 对于锯齿状阵列,您可以执行arr.SelectMany(x=>x).ToList()
  • 之类的操作
  • T[,]上,您只需arr.ToList(),因为IEnumerable<T> T[,]会返回2D数组中的所有元素。看起来像2D数组仅实现IEnumerable但不实现IEnumerable<T>,因此您需要插入一个Cast<double>之类的已建议的其他编码器。由于拳击,这将使它更慢。

唯一可以使代码比天真循环更快的是计算元素的数量并使用正确的容量构建List,因此它不需要增长。
如果您的数组是矩形,则可以获得width*height的大小,使用锯齿状数组可能会更难。

int width=1000;
int height=3000;
double[,] arr=new double[width,height];
List<double> list=new List<double>(width*height);
int size1=arr.GetLength(1);
int size0=arr.GetLength(0);
for(int i=0;i<size0;i++)
{  
  for(int j=0;j<size1;j++)
    list.Add(arr[i,j]);
}

理论上,可以使用私有反射和不安全的代码来使原始内存复制更快一些。但我强烈反对这一点。