如何在c#中创建和管理2D类数组列表对象
本文关键字:2D 数组 列表 对象 管理 创建 | 更新日期: 2023-09-27 18:04:12
我需要一个2D数组,它可以在所有方向上扩展,并严格跟踪每个元素的整体定位,但在读取时效率最高。
我面对的用例如下:我正在将二维构造板块相互碰撞。当它们碰撞时,它们可以收缩,也可以增长,或者两者都不能。在每次迭代中,所有元素都有可能被访问,因此读取时间非常重要。它们必须能够在所有方面生长/收缩,并且可以包含孔和凸结构。
内存不是一个大问题,但我想把它保存在我可以。我主要关心的是速度,因为旧的概念证明是松散地基于c++编写的,需要15分钟才能运行,而且我正在大量添加原始概念。
我最初想使用带有坐标的字典,但这带来了读取时间的问题;当请求字典中没有键的东西时,字典的速度很慢,这种情况经常发生。
我正在考虑使用List<List<MyCLass>>
结构现在,使用空类对象的空位置。
我的另一个想法是使用一个代数数组(y * stride + x),但我宁愿避免它的复杂性,因为它很难构建和维护。
那么,从本质上讲,在c#中不断访问和频繁修改一个非常大的2D数据集的最佳方法是什么?
编辑:要求;每个数组的大小可能在50x50到1000x1000之间,并且在任何给定时间都有10-30个数组。整个"世界"的大小将是512x512到4096x4096(在模拟开始时设置),最大重叠约为20%(不包括边缘情况)。然而,在笛卡尔系统中,每个2D板的50%以上将是空白空间,所以对于统一的大小,这意味着数组的实际大小将是它的两倍,所以最多大约有20,132,659个非空数组元素,并且比模拟中的空元素总数略少。
我可以接受这个程序花几个小时来完成一个模拟,但我担心它会花几天的时间。这就是为什么我试图想出处理这些数据集的最佳方法
如果您坚持使用可扩展的2D数组(矩阵),那么请考虑以下事项:
public class Matrix<T> : Collection<T>
{
int row_count, col_count;
List<T> _list; //reference to inner list
T[] _items; //reference to inner array within inner list
public Matrix(int row_count, int col_count)
: this(row_count, col_count, new T[row_count*col_count])
{
if(row_count==0||col_count==0)
{
throw new NotSupportedException();
}
}
Matrix(int row_count, int col_count, T[] values)
: base(new List<T>(values))
{
// internal data arranged in 1D array, by rows.
this._list=base.Items as List<T>;
this.row_count=row_count;
this.col_count=col_count;
LinkInnerArray();
}
private void LinkInnerArray()
{
this._items=typeof(List<T>).GetField("_items",
System.Reflection.BindingFlags.NonPublic
|System.Reflection.BindingFlags.Instance).GetValue(base.Items) as T[];
}
public int RowCount { get { return row_count; } }
public int ColCount { get { return col_count; } }
public T[] Elements { get { return _list.ToArray(); } }
public T this[int row, int col]
{
get { return base[col_count*row+col]; }
set { base[col_count*row+col]=value; }
}
public T[] GetRow(int row)
{
if(row<0||row>=row_count) new IndexOutOfRangeException();
T[] result=new T[col_count];
lock(_items)
{
// fast array copy
Array.Copy(_items, col_count*row, result, 0, result.Length);
}
return result;
}
public T[] GetColumn(int column)
{
if(column<0||column>=col_count) new IndexOutOfRangeException();
T[] result=new T[row_count];
lock(_items)
{
// No shortcut exists, only if C# was more like FORTRAN
for(int i=0; i<row_count; i++)
{
result[i]=base[col_count*i+column];
}
}
return result;
}
public void SetRow(int row, params T[] values)
{
if(values==null||values.Length==0) return;
if(row<0||row>=row_count) new IndexOutOfRangeException();
// fast array copy
lock(_items)
{
Array.Copy(values, 0, _items, col_count*row, values.Length);
}
}
public void SetColumn(int column, params T[] values)
{
if(values==null||values.Length==0) return;
if(column<0||column>=col_count) new IndexOutOfRangeException();
lock(_items)
{
// No shortcut exists, only if C# was more like FORTRAN
for(int i=0; i<values.Length; i++)
{
base[col_count*i+column]=values[i];
}
}
}
public void AddRow(params T[] new_row)
{
lock(_list)
{
// add array to last row
T[] row=new T[col_count];
Array.Copy(new_row, 0, row, 0, new_row.Length);
_list.AddRange(row);
LinkInnerArray();
this.row_count++;
}
}
public void AddColumn(params T[] new_column)
{
lock(_list)
{
// go add an item on end of each row
for(int i=row_count-1; i>=0; i--)
{
T item=i<new_column.Length?new_column[i]:default(T);
_list.Insert(col_count*i+row_count-1, item);
}
LinkInnerArray();
this.col_count++;
}
}
public Matrix<R> Transform<R>(Func<T, R> operation)
{
R[] values=new R[row_count*col_count];
for(int i=0; i<values.Length; i++)
{
values[i]=operation(base[i]);
}
return new Matrix<R>(row_count, col_count, values);
}
public Matrix<R> Combine<R>(Matrix<T> other, Func<T, T, R> operation)
{
R[] values=new R[row_count*col_count];
for(int i=0; i<values.Length; i++)
{
values[i]=operation(base[i], other[i]);
}
return new Matrix<R>(row_count, col_count, values);
}
}
class Program
{
static void Main(string[] args)
{
int N=4;
var A=new Matrix<int>(N, N);
// initialize diagonal
for(int i=0; i<N; i++)
{
A[i, i]=1;
}
// A =
// | 1 0 0 0 |
// | 0 1 0 0 |
// | 0 0 1 0 |
// | 0 0 0 1 |
A.AddRow(5, 4, 3, 2);
A.AddColumn(1, 2, 3, 4, 5);
// A =
// | 1 0 0 0 1 |
// | 0 1 0 0 2 |
// | 0 0 1 0 3 |
// | 0 0 0 1 4 |
// | 5 4 3 2 5 |
var B=A.Transform(delegate(int x) { return 5-x; });
// B =
// | 4 5 5 5 4 |
// | 5 4 5 5 3 |
// | 5 5 4 5 2 |
// | 5 5 5 4 1 |
// | 0 1 2 3 0 |
var C=A.Combine(B, delegate(int x, int y) { return y-x; });
// C =
// | 3 5 5 5 3 |
// | 5 3 5 5 1 |
// | 5 5 3 5 -1 |
// | 5 5 5 3 -3 |
// |-5 -3 -1 1 -5 |
}
}
这取决于你如何阅读你的集合。
例如,如果循环遍历每个元素,则List总是比dictionary快。
List<List<Item>> collection = new List<List<Items>>();
//add items
for(List l : collection){
for(Item i : l){
//do something with item
}
}
如果你在查找索引,基于一个"键"值,那么字典将总是更快(常量vs线性)。
Dictionary<String,List<Item>> collection = new Dictionary<String,List<Items>>();
//add items
List<String> keysWeNeedToWorkOn = new List<String>();
//add keys we care about
for(String key : keysWeNeedToWorkOn){
for(Item i : collection.get(key)){
// do something with this item
}
}