Maneira rápida de converter uma matriz bidimensional em uma lista (unidimensional)

Eu tenho dois matriz dimensional – e eu preciso convertê-lo em uma lista (mesmo object)

Eu não quero fazer isso com o loop ‘for’ ou ‘foreach’, que pegará cada elemento e o adicionará à Lista.

Existe alguma outra maneira de fazer isso?

obrigado

Para converter o double[, ] para List ? Se você está procurando um one-liner, aqui vai

 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() 

Mas, se você está procurando algo eficiente, prefiro dizer que você não usa esse código.
Por favor, siga uma das duas respostas mencionadas abaixo. Ambos estão implementando técnicas muito melhores.

Bem, você pode usar um tipo de cópia “blit”, embora isso signifique fazer uma cópia extra 🙁

 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); 

Se você está feliz com uma matriz unidimensional, é claro, apenas ignore a última linha 🙂

Buffer.BlockCopy é implementado como um método nativo que eu esperaria usar uma cópia extremamente eficiente após a validação. O List constructor que aceita um IEnumerable é otimizado para o caso em que implementa IList , como double[] faz. Ele criará uma matriz de apoio do tamanho certo e solicitará que ele copie a si mesmo nessa matriz. Espero que isso use o Buffer.BlockCopy ou algo similar também.

Aqui está um benchmark rápido das três abordagens (for loop, Cast().ToList() e 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; } } 

Resultados (tempos em milissegundos);

 LINQ: 253463 For loop: 9563 Block copy: 8697 

EDIT: Tendo alterado o loop para chamar array.GetLength() em cada iteração, o loop for e a cópia do bloco demoram ao mesmo tempo.

Um loop for é o caminho mais rápido.

Você pode ser capaz de fazer isso com o LINQ, mas isso será mais lento. E enquanto você não escreve um loop, sob o capô ainda há um loop.

  • Para um arranjo irregular, você provavelmente pode fazer algo como arr.SelectMany(x=>x).ToList() .
  • Em T[,] você pode simplesmente fazer arr.ToList() pois o IEnumerable de T[,] retorna todos os elementos no array 2D. Parece que o array 2D implementa apenas IEnumerable mas não IEnumerable então você precisa inserir um Cast como um outro codificador sugerido. Isso tornará ainda mais lento devido ao boxe.

A única coisa que pode tornar o código mais rápido do que o loop ingênuo é calcular o número de elementos e construir a List com a capacidade correta, para que não precise crescer.
Se sua matriz é retangular, você pode obter o tamanho como width*height , com matrizes serrilhadas pode ser mais difícil.

 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 

Em teoria, pode ser possível usar reflection privada e código não seguro para torná-lo um pouco mais rápido fazendo uma cópia de memory bruta. Mas eu aconselho fortemente contra isso.