views:

63

answers:

5

I've come across a problem where I'd like to create an Array table. That is a 2 dimensional array where the number of rows and columns are known at runtime before the table needs to be created. The number of columns is the same for all rows.

Once the array is created I'd like to operate on just 1 dimension of that array. Perhaps pass a reference to an method.

Here is a fictional example:

// Create a table 3x3 table.
int[,] DistanceTable = new int[3, 3];
DistanceTable[0, 0] = 0;
DistanceTable[1, 1] = 0;
DistanceTable[2, 2] = 0;

DistanceTable[0, 1] = 10;
DistanceTable[0, 2] = 40;

DistanceTable[1, 0] = 10;
DistanceTable[1, 2] = 25;

DistanceTable[2, 0] = 40;
DistanceTable[2, 1] = 25;

// Why can't I do this?
int[] twos = DistanceTable[2];

If I were to use a JaggedArray (Array-of-Arrays) it lets me do this. But I don't need a JaggedArray because my multidemsional array always has the same number of columns for each row.

Is it possible to do this? If not why?

Thanks

+2  A: 

This is not possible; multi-dimensional arrays do not work like that.

In general, you should always use jagged arrays; they're faster.
(The JITter will generate raw memory access instructions instead of method calls)

SLaks
why does the JITter not do this for multi-dimensional arrays?
Lou Franco
+1  A: 

There is only one array object with a multidimensional array whereas a a jagged array is a nesting of multiple distinct array objects. There is not a 1-1 match-up or method of extraction (that does not involve using a wrapper).

pst
+2  A: 

No. Multidimensional arrays differ from Jagged arrays in that they are stored sequentially, in one block of memory, using Row-Major ordering.

Because of this, pulling out one "column" of data requires skipping in order to pull this out.

Jagged arrays, on the other hand, are an array of references to a second array. This makes it easy to pull out individual "arrays" from the jagged array.

But I don't need a JaggedArray because my multidemsional array always has the same number of columns for each row.

Jagged arrays in .NET have some huge performance optimizations. They often outperform multidimensional arrays. This is why most code analysis routines will suggest conversion to a jagged array from a two dimensional array. It would be worth considering this even if you don't "need" it.

Reed Copsey
Interesting. I would have thought multidimensional arrays would be more efficient (hence my desire to use them). Thanks for the great explanation! Jagged Array it is!
Justin
@Justin: Yeah - it's very counter-intuitive.
Reed Copsey
@Justin: Jagged is usually faster - but **does** use more memory.
Reed Copsey
A: 

You could make an extension method on your DictionaryTable object that allows you to specify the row?

public static class IntArrayExt
{
    public static int[] Row(this int[,] array, int row)
    {
        int[] newArray = new int[3];
        for (int i = 0; i < array.Length; i++)
        {
            newArray[i] = array[row, i];
        }
        return newArray;
    }
}

int[,] distanceTable = new int[3, 3];
distanceTable[0, 0] = 0;
distanceTable[1, 1] = 0;
distanceTable[2, 2] = 0;

distanceTable[0, 1] = 10;
distanceTable[0, 2] = 40;

distanceTable[1, 0] = 10;
distanceTable[1, 2] = 25;

distanceTable[2, 0] = 40;
distanceTable[2, 1] = 25;

int[] twos = distanceTable.Row(2);

You could make another extension method if you want to get a column.

Thorin
+1  A: 

I'm sorry, this should really be a comment to http://stackoverflow.com/questions/3964299/how-do-i-get-a-reference-to-a-single-dimension-of-a-multidemensional-array-in-c/3964333#3964333 but I'm not allowed to comment yet..

Anyway, the reason to why the performance is better with a jagged array is easy to understand after some explanation: Let's examine a multidimensional array:

{{0, 1, 2}, {3, 4, 5}, {6, 7, 8}}

In memory it's stored something like this: {0, 1, 2, 3, 4, 5, 6, 7, 8}.

Now, suppose you want to access [0, 0], where in memory are we going to read? We have to calculate the address: y * 3 + x => 0 * 3 + 0 => 0. After that, we can go on and do the actual read. In case we want to read the whole line, we have to do this math over and over again.

Instead, look at a jagged array, in memory it's stored something like this:

a: {0, 1, 2} b: {3, 4, 5} c: {6, 7, 8} {ref: a, ref:b, ref:c}

Suppose we want to access [0][0], where in memory are we going to read? First, let's get a reference to array [0]. And then get the content of cell [0]. Done. In case we want to read the whole line, we only need to increment the pointer by one.

If we are going to iterate through the whole "array" instead of just one row, it's still the same performance benefit for jagged arrays.

There's one exception though: iterate columns. That's really bad for jagged arrays since we are going to do a relative expensive memory-read for each and every access. Not good.

If you feel that it's a problem there's one method left: A one-dimensional array! In this case we use the theory behind the multidimensional array (y * rowLength + x), and use some really simple math; To iterate a row: just increment by one, to iterate a column: just increment by rowLength.

Onkelborg
The calculating offset seems trivial (and certainly no more expensive than an indirect look-up?); I suspect the performance improvements seen are due to better cache hits, specific optimization cases, and usage patterns which may also be related to how the two approaches 'draw out' common iteration patterns. And welcome to SO :-)
pst
In most applications, it's trivial, however, when you are iterating big arrays, and doesn't perform much, there's performance to get there. But of course, don't overdo.. :) And thanks :)
Onkelborg