【问题标题】:How do I get a reference to a single dimension of a Multidemensional Array in C#?如何在 C# 中获取对多维数组的单个维度的引用?
【发布时间】:2010-10-18 23:32:32
【问题描述】:

我遇到了一个问题,我想创建一个数组表。这是一个二维数组,其中行数和列数在需要创建表之前在运行时是已知的。所有行的列数相同。

创建数组后,我只想对该数组的一维进行操作。也许传递对方法的引用。

这是一个虚构的例子:

// 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];

如果我要使用 JaggedArray (Array-of-Arrays),我可以这样做。但我不需要 JaggedArray,因为我的多用途数组每行的列数始终相同。

可以这样做吗?如果不是为什么?

谢谢

【问题讨论】:

  • 你需要一个锯齿状数组。不要通过围棋,没有它们就不要收取 200 美元。

标签: c# arrays multidimensional-array jagged-arrays


【解决方案1】:

没有。多维数组与锯齿状数组的不同之处在于,它们按顺序存储在一块内存中,使用Row-Major ordering

因此,拉出一“列”数据需要跳过才能将其拉出。

另一方面,锯齿状数组是对第二个数组的引用数组。这使得从锯齿状数组中提取单个“数组”变得容易。

但我不需要 JaggedArray,因为我的多用途数组每行的列数始终相同。

.NET 中的锯齿状数组有一些巨大的性能优化。它们通常优于多维数组。这就是为什么大多数代码分析例程会建议从二维数组转换为锯齿状数组的原因。即使您“不需要”它,也值得考虑。

【讨论】:

  • 有趣。我原以为多维数组会更有效(因此我希望使用它们)。感谢您的精彩解释!就是锯齿状阵列!
  • @Justin:是的 - 这非常违反直觉。
  • @Justin:Jagged 通常更快 - 但确实使用更多内存。
【解决方案2】:

这是不可能的;多维数组不是这样工作的。

一般来说,您应该始终使用锯齿状数组;他们更快。
(JITter 将生成原始内存访问指令而不是方法调用)

【讨论】:

  • 为什么 JITter 不对多维数组这样做?
【解决方案3】:

只有一个数组对象具有多维数组,而交错数组是多个 不同 数组对象的嵌套。没有 1-1 的比赛或提取方法(不涉及使用包装器)。

【讨论】:

    【解决方案4】:

    对不起,这应该是对How do I get a reference to a single dimension of a Multidemensional Array in C#? 的评论,但我还不能评论..

    不管怎样,锯齿状数组的性能更好的原因在一些解释之后很容易理解:让我们检查一个多维数组:

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

    它在内存中存储如下:{0, 1, 2, 3, 4, 5, 6, 7, 8}。

    现在,假设您要访问 [0, 0],我们要读取内存中的哪个位置?我们必须计算地址:y * 3 + x => 0 * 3 + 0 => 0。之后,我们可以继续进行实际读取。如果我们想阅读整行,我们必须一遍又一遍地做这个数学运算。

    相反,查看一个锯齿状数组,它在内存中存储如下:

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

    假设我们要访问[0][0],我们要读取内存的哪个位置?首先,让我们获取对数组 [0] 的引用。然后获取单元格[0]的内容。完毕。如果我们想读取整行,只需将指针加一即可。

    如果我们要遍历整个“数组”而不是只遍历一行,对于锯齿状数组来说,它仍然具有相同的性能优势。

    但有一个例外:迭代列。这对于锯齿状数组来说真的很糟糕,因为我们将为每次访问进行相对昂贵的内存读取。不好。

    如果您觉得这是个问题,那么剩下一种方法:一维数组!在这种情况下,我们使用多维数组(y * rowLength + x)背后的理论,并使用一些非常简单的数学;迭代一行:只增加一,迭代一列:只增加rowLength。

    【讨论】:

    • 计算偏移量似乎微不足道(当然不会比间接查找更昂贵?);我怀疑所看到的性能改进是由于更好的缓存命中、特定的优化案例和使用模式,这也可能与这两种方法如何“提取”常见的迭代模式有关。欢迎来到 SO :-)
    • 在大多数应用程序中,它是微不足道的,但是,当您迭代大数组并且执行不太多时,性能可以达到那里。但是,当然,不要过度.. :) 谢谢 :)
    【解决方案5】:

    您可以在 DictionaryTable 对象上创建一个扩展方法,允许您指定行吗?

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

    如果你想得到一个列,你可以做另一种扩展方法。

    【讨论】:

      猜你喜欢
      • 2012-11-28
      • 1970-01-01
      • 2013-08-03
      • 2018-04-25
      • 1970-01-01
      • 2020-07-24
      • 2023-03-29
      • 2012-03-13
      • 1970-01-01
      相关资源
      最近更新 更多