簡體   English   中英

將二維數組轉換為List(一維)的快速方法

[英]Fast way to convert a two dimensional array to a List ( one dimensional )

我有一個二維數組,我需要將其轉換為List(相同的對象)。 我不想使用forforeach循環來執行它,它將獲取每個元素並將其添加到List。 還有其他方法嗎?

好吧,你可以使用“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被實現為我期望驗證后使用極其高效復制一個本地方法。 接受IEnumerable<T>List<T> constructor針對它實現IList<T>的情況進行了優化,正如double[]所做的那樣。 它將創建一個正確大小的后備數組,並要求它將自身復制到該數組中。 希望這也會使用Buffer.BlockCopy或類似的東西。

這是三種方法的快速基准(for循環, 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;
    }
}

結果(以毫秒為單位的時間);

LINQ: 253463
For loop: 9563
Block copy: 8697

編輯:更改了for循環以在每次迭代時調用array.GetLength() ,for循環和塊復制大約在同一時間。

要將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();


但是,如果您正在尋找有效的東西,我寧願說您不使用此代碼。
請遵循下面提到的兩個答案中的任何一個。 兩者都在實施更好的技術。

for循環是最快的方法。

您可以使用LINQ來完成它,但這會更慢。 雖然你自己不編寫循環,但仍然存在一個循環。

  • 對於鋸齒狀數組,您可以執行類似arr.SelectMany(x=>x).ToList()
  • T[,]你可以簡單地做arr.ToList()因為T[,]IEnumerable<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]);
}

從理論上講,可能會使用私有反射和不安全的代碼來使原始內存復制更快一些。 但我強烈建議不要這樣做。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM