将二维数组转换为List(一维)的快速方法
我有一个二维数组,我需要将其转换为List(相同的对象)。 我不想用for
或foreach
循环来完成它,它将获取每个元素并将其添加到List中。 还有其他方法吗?
要将double[, ]
转换为List
? 如果你正在寻找单行,那么就去了
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 lst = d.Cast ().ToList()
但是,如果您正在寻找有效的东西,我宁愿说您不使用此代码。
请遵循下面提到的两个答案之一。 两者都在实施更好的技术。
好吧,你可以使用“blit”类型的副本,虽然它确实意味着制作一个额外的副本:(
double[] tmp = new double[array.GetLength(0) * array.GetLength(1)]; Buffer.BlockCopy(array, 0, tmp, 0, tmp.Length * sizeof(double)); List list = new List (tmp);
如果您对单维数组感到满意,那么只需忽略最后一行:)
Buffer.BlockCopy
是作为本机方法实现的,我希望在validation后使用非常有效的复制。 接受IEnumerable
的List
针对它实现IList
的情况进行了优化,正如double[]
所做的那样。 它将创建一个正确大小的后备数组,并要求它将自身复制到该数组中。 希望这也会使用Buffer.BlockCopy
或类似的东西。
这是三种方法的快速基准(for循环, Cast
和Buffer.BlockCopy):
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 UsingCast(double[,] array) { return array.Cast ().ToList(); } static List UsingForLoop(double[,] array) { int width = array.GetLength(0); int height = array.GetLength(1); List ret = new List (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 UsingBlockCopy(double[,] array) { double[] tmp = new double[array.GetLength(0) * array.GetLength(1)]; Buffer.BlockCopy(array, 0, tmp, 0, tmp.Length * sizeof(double)); List list = new List (tmp); return list; } }
结果(以毫秒为单位的时间);
LINQ: 253463 For loop: 9563 Block copy: 8697
编辑:更改了for循环以在每次迭代时调用array.GetLength()
,for循环和块复制大约在同一时间。
for
循环是最快的方法。
您可以使用LINQ来完成它,但这会更慢。 虽然你自己不写循环,但仍然存在一个循环。
- 对于锯齿状数组,您可以执行类似
arr.SelectMany(x=>x).ToList()
。 -
在看起来2D数组只实现T[,]
你可以简单地做arr.ToList()
因为T[,]
的IEnumerable
返回2D数组中的所有元素。IEnumerable
而不是IEnumerable
所以你需要插入一个Cast
就像建议的其他编码器一样。 由于拳击,这将使它更慢。
唯一可以使代码比原始循环更快的是计算元素的数量并使用正确的容量构造List,因此它不需要增长。
如果您的数组是矩形,您可以获得width*height
的大小,使用锯齿状数组可能会更难。
int width=1000; int height=3000; double[,] arr=new double[width,height]; List list=new List (width*height); int size1=arr.GetLength(1); int size0=arr.GetLength(0); for(int i=0;i
从理论上讲,可能会使用私有reflection和不安全的代码来使原始内存复制更快一些。 但我强烈建议不要这样做。