简体   繁体   中英

How to copy a row of values from a 2D array into a 1D array?

We have the following object

int [,] oGridCells;

which is only used with a fixed first index

int iIndex = 5;
for (int iLoop = 0; iLoop < iUpperBound; iLoop++)
{
  //Get the value from the 2D array
  iValue = oGridCells[iIndex, iLoop];

  //Do something with iValue
}

Is there a way in .NET to convert the values at a fixed first index into a single dimension array (other than by looping the values)?

I doubt it would speed up the code (and it may well make it slower) if the array is only being looped once. But if the array was being heavily manipulated then a single dimension array would be more efficient than a multi dimension array.

My main reason for asking the question is to see if it can be done and how, rather than using it for production code.

The following code demonstrates copying 16 bytes (4 ints) from a 2-D array to a 1-D array.

int[,] oGridCells = {{1, 2}, {3, 4}};
int[] oResult = new int[4];
System.Buffer.BlockCopy(oGridCells, 0, oResult, 0, 16);

You can also selectively copy just 1 row from the array by providing the correct byte offsets. This example copies the middle row of a 3-row 2-D array.

int[,] oGridCells = {{1, 2}, {3, 4}, {5, 6}};
int[] oResult = new int[2];
System.Buffer.BlockCopy(oGridCells, 8, oResult, 0, 8);

Edit:

I realized there is a way! Granted, it's probably not worth it. Use unsafe code . Full example, showing both ways, with unsafe below:

public class MultiSingleUnsafe
{
    public static unsafe void Main(String[] a)
    {
    int rowCount = 6;
    int iUpperBound = 10;
    int [,] oGridCells = new int[rowCount, iUpperBound];

    int iIndex = rowCount - 2; // Pick a row.

    for(int i = 0; i < iUpperBound; i++)
    {
        oGridCells[iIndex, i] = i;
    }

    for (int iLoop = 0; iLoop < iUpperBound; iLoop++)
    {
        //Get the value from the 2D array
        int iValue = oGridCells[iIndex, iLoop];
        Console.WriteLine("Multi-dim array access iValue: " + iValue);
        //Do something with iValue
    }
    
    fixed(int *lastRow = &(oGridCells[iIndex,0]))
    {   
        for (int iLoop = 0; iLoop < iUpperBound; iLoop++)
        {
        int iValue = lastRow[iLoop];
        Console.WriteLine("Pointer access iValue: " + iValue);
        }
    }
    }
}

There is no way I know to cast a multiple-dimensional array into a single-dimensional one in C#. Of course, you could create a new single-dimensional array and copy into it. But I don't think this will get a performance benefit even if you loop over the values multiple times. As Daren said, internally it's all pointer arithmetic anyway. If you want to be certain, profile it.

You can try this:

 int[,] twoD = new int[2,2];
 twoD[0, 0] = 1;
 twoD[0, 1] = 2;
 twoD[1, 0] = 3;
 twoD[1, 1] = 4;

 int[] result = twoD.Cast<int>().Select(c => c).ToArray();

The result will be an integer array with data:

1, 2, 3, 4

I'd be suprised if it were possible: I bet oGridCells[iIndex, iLoop] is just a sort of shorthand (internally, in MSIL) for oGridCells[iIndex * iLoop] , and that multidimensional arrays are syntactic sugar for this.

To answer your question: No. You will have to loop the values.

You cannot get a reference to each array. You can, however, use a jagged array .

"But if the array was being heavily manipulated then a single dimension array would be more efficient than a multi dimension array."

I did some profiling of exactly this last summer and was surprised to see no significant difference in performance between a 2D and 1D array.

I didn't test the performance of a jagged array.

It seems it's about 30% faster to use Span.CopyTo on .NET Core and .NET 5+, than to use Buffer.BlockCopy as in @BlueMonkMN's answer .

public static T[] CopyToArray<T>(ref T start, int length)
{
    var target = new T[length];
    var span = MemoryMarshal.CreateReadOnlySpan(ref start, length);
    span.CopyTo(target.AsSpan());
    return target;
}

You would use it like this (obviously ensure you don't overrun the array, as there are no bounds checks).

var singleArray = CopyToArray(ref oGridCells[0, 0], oGridCells.Length);

dotnetfiddle

BenchmarkDotNet results

Method Mean Error StdDev
BlueMonkMN 18.84 ns 0.220 ns 0.206 ns
Charlieface 12.31 ns 0.196 ns 0.183 ns

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM