在c#中,多维数组double[,]和数组的数组double[][]有什么区别?

如果有区别,每一种的最佳用途是什么?


当前回答

我正在解析ildasm生成的.il文件,以构建用于进行转换的程序集、类、方法和存储过程的数据库。我遇到了下面的问题,打断了我的解析。

.method private hidebysig instance uint32[0...,0...] 
        GenerateWorkingKey(uint8[] key,
                           bool forEncryption) cil managed

Serge Lidin于2006年出版的《Expert . net 2.0 IL汇编器》一书,第8章,原始类型和签名,149-150页解释了这一点。

<type>[]被称为<type>的Vector,

<type>[<bounds> [<bounds>**]]被称为<type>的数组

**表示可重复,[]表示可选。

示例:Let <type> = int32。

1) int32[……]是一个具有未定义的下界和大小的二维数组

2) int32[2…5]是一个下界为2,大小为4的一维数组。

3) int32[0, 0…]是一个下界为0且大小未定义的二维数组。

Tom

其他回答

数组的数组(锯齿数组)比多维数组更快,可以更有效地使用。多维数组有更好的语法。

如果你使用交错数组和多维数组写一些简单的代码,然后用IL反汇编器检查编译后的程序集,你会发现从交错数组(或一维数组)中存储和检索是简单的IL指令,而多维数组的相同操作是方法调用,总是比较慢。

考虑以下方法:

static void SetElementAt(int[][] array, int i, int j, int value)
{
    array[i][j] = value;
}

static void SetElementAt(int[,] array, int i, int j, int value)
{
    array[i, j] = value;
}

他们的IL将如下:

.method private hidebysig static void  SetElementAt(int32[][] 'array',
                                                    int32 i,
                                                    int32 j,
                                                    int32 'value') cil managed
{
  // Code size       7 (0x7)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ldarg.1
  IL_0002:  ldelem.ref
  IL_0003:  ldarg.2
  IL_0004:  ldarg.3
  IL_0005:  stelem.i4
  IL_0006:  ret
} // end of method Program::SetElementAt

.method private hidebysig static void  SetElementAt(int32[0...,0...] 'array',
                                                    int32 i,
                                                    int32 j,
                                                    int32 'value') cil managed
{
  // Code size       10 (0xa)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ldarg.1
  IL_0002:  ldarg.2
  IL_0003:  ldarg.3
  IL_0004:  call       instance void int32[0...,0...]::Set(int32,
                                                           int32,
                                                           int32)
  IL_0009:  ret
} // end of method Program::SetElementAt

当使用锯齿状数组时,可以轻松地执行行交换和行大小调整等操作。也许在某些情况下使用多维数组会更安全,但即使Microsoft FxCop也告诉我们,当您使用锯齿数组来分析您的项目时,应该使用锯齿数组而不是多维数组。

简单地说,多维数组类似于DBMS中的表。 Array of Array(锯齿状数组)允许每个元素保存另一个相同类型的可变长度数组。

因此,如果您确定数据结构看起来像一个表(固定的行/列),您可以使用多维数组。锯齿数组是固定的元素&每个元素可以容纳一个可变长度的数组

例如Psuedocode:

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

我们可以把上面的表格看成一个2x2的表格:

1 | 2 3 | 4

int[][] jagged = new int[3][]; 
jagged[0] = new int[4] {  1,  2,  3,  4 }; 
jagged[1] = new int[2] { 11, 12 }; 
jagged[2] = new int[3] { 21, 22, 23 }; 

可以把上面的代码看成每一行都有可变的列数:

1 | 2 | 3 | 4 11 | 12 21 | | 22, 23

这可能在上面的回答中提到过,但没有明确地提到:对于锯齿数组,您可以使用array[row]引用整行数据,但这对于多维数组是不允许的。

交错数组是数组的数组,或者每一行包含一个自己的数组的数组。

这些数组的长度可以不同于其他行的长度。

声明和分配数组的数组

与常规多维数组相比,锯齿数组声明的唯一不同之处在于,我们不只有一对括号。对于锯齿状数组,每个维度都有一对括号。我们这样分配它们:

int [] [] exampleJaggedArray; jaggedArray = new int[2][]; jaggedArray[0] = new int[5]; jaggedArray[1] = new int[3];

初始化数组的数组

int[][] exampleJaggedArray = { New int[] {5,7,2}, New int[] {10,20,40}, 新的int[] {3,25} };

内存分配

锯齿数组是引用的聚合。锯齿状数组不直接包含任何数组,而是有指向它们的元素。大小是未知的,这就是为什么CLR只保留对内部数组的引用。在我们为锯齿状数组的一个数组元素分配内存之后,引用开始指向动态内存中新创建的块。

变量exampleJaggedArray存储在程序的执行堆栈中,并指向动态内存中的一个块,该块包含对内存中其他三个块的三个引用序列;它们每个都包含一个整数数组——锯齿数组的元素:

使用基于John Leidegren的测试,我使用。net 4.7.2对结果进行了基准测试,这是与我的目的相关的版本,我认为我可以分享。我最初是从dotnet核心GitHub存储库中的这条注释开始的。

随着数组大小的变化,性能似乎有很大变化,至少在我的设置中是这样,1个处理器xeon, 4physical 8logical。

W =初始化一个数组,并将int I * j放入其中。 Wr = do w,然后在另一个循环中设置int x为[i,j]

随着数组大小的增长,多维似乎表现得更好。

Size rw Method Mean Error StdDev Gen 0/1k Op Gen 1/1k Op Gen 2/1k Op Allocated Memory/Op
1800*500 w Jagged 2.445 ms 0.0959 ms 0.1405 ms 578.1250 281.2500 85.9375 3.46 MB
1800*500 w Multi 3.079 ms 0.2419 ms 0.3621 ms 269.5313 269.5313 269.5313 3.43 MB
2000*4000 w Jagged 50.29 ms 3.262 ms 4.882 ms 5937.5000 3375.0000 937.5000 30.62 MB
2000*4000 w Multi 26.34 ms 1.797 ms 2.690 ms 218.7500 218.7500 218.7500 30.52 MB
2000*4000 wr Jagged 55.30 ms 3.066 ms 4.589 ms 5937.5000 3375.0000 937.5000 30.62 MB
2000*4000 wr Multi 32.23 ms 2.798 ms 4.187 ms 285.7143 285.7143 285.7143 30.52 MB
1000*2000 wr Jagged 11.18 ms 0.5397 ms 0.8078 ms 1437.5000 578.1250 234.3750 7.69 MB
1000*2000 wr Multi 6.622 ms 0.3238 ms 0.4847 ms 210.9375 210.9375 210.9375 7.63 MB

更新:最后两个测试用双[,]代替int[,]。考虑到误差,这种差异显得很显著。对于int,锯齿与md的平均比率在1.53x和1.86x之间,对于双精度,它是1.88x和2.42x。

Size rw Method Mean Error StdDev Gen 0/1k Op Gen 1/1k Op Gen 2/1k Op Allocated Memory/Op
1000*2000 wr Jagged 26.83 ms 1.221 ms 1.790 ms 3062.5000 1531.2500 531.2500 15.31 MB
1000*2000 wr Multi 12.61 ms 1.018 ms 1.524 ms 156.2500 156.2500 156.2500 15.26 MB