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

Yan*_*hof 24 c# arrays list type-conversion

我有一个二维数组,我需要将其转换为List(相同的对象).我不想使用forforeach循环来获取每个元素并将其添加到List.还有其他方法吗?

Jon*_*eet 56

好吧,你可以使用"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);
Run Code Online (Sandbox Code Playgroud)

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

Buffer.BlockCopy被实现为我一个本地方法希望验证后使用极其高效复制.在List<T> constructor其中接受一个IEnumerable<T>是对于其中它实现的情况下最优化IList<T>,如double[]做.它将创建一个正确大小的后备数组,并要求它将自身复制到该数组中.希望这也会使用Buffer.BlockCopy或类似的东西.

这是三种方法的快速基准(for loop Cast<double>().ToList(),和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<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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

LINQ: 253463
For loop: 9563
Block copy: 8697
Run Code Online (Sandbox Code Playgroud)

编辑:更改了for循环以array.GetLength()在每次迭代时调用,for循环和块复制大约在同一时间进行.

  • 这个问题的主要问题是它可以在大对象堆上留下一个大的临时数组. (2认同)

nav*_*een 32

要转换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();
Run Code Online (Sandbox Code Playgroud)


但是,如果您正在寻找有效的东西,我宁愿说您不使用此代码.
请遵循下面提到的两个答案中的任何一个.两者都在实施更好的技术.

  • 在我1000 x 1000阵列的快速基准测试中,这比for循环或Buffer.BlockCopy解决方案的执行速度快*30倍*.考虑到标题的"快速"部分,我很惊讶它被接受了. (7认同)
  • 除了其他一切之外,最终会在数组中对每个`double`进行装箱......它的表现会很糟糕. (3认同)
  • @downvoters:谢谢你告诉我,我知道的比JonSkeet少.:)请理解我没有删除答案,因为OP在某处使用了这段代码,并对此感到满意.告诉我,你们有多少人在企业级工作?滑稽 (2认同)
  • @naveen:我没有看到这方面的真实证据,并且鉴于OP可以通过复制和粘贴它们并调整到他的变量名来使用任何答案,它们在所有定义中同样"快".即使您认为这是最可能的意图,您的答案也没有表明所涉及的低效率,这对于为所有*读者提供服务而不仅仅是原始海报. (2认同)

Cod*_*aos 11

一个for循环是最快的方法.

您可以使用LINQ来完成它,但这会更慢.虽然你自己不编写循环,但仍然存在一个循环.

  • 对于锯齿状阵列,你可能会做类似的事情arr.SelectMany(x=>x).ToList().
  • T[,]你可以简单地做arr.ToList()IEnumerable<T>T[,]二维数组中返回的所有元素.看起来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]);
}
Run Code Online (Sandbox Code Playgroud)

从理论上讲,可能会使用私有反射和不安全的代码来使原始内存复制更快一些.但我强烈建议不要这样做.