从这两地方抄的:
线性表(2)——顺序表的动态实现 – 柒氿 (whitedew.work)
C#高性能低GC非托管动态扩容数组 – 知乎 (zhihu.com)
为了操作内存用了unsafe开发,有很多瑕疵和缺点。但我懒得改了,差不多能跑,使用中基本不会出错就行了(反正也不用)。
在unity里测试的,所以输出用了Debug.log系列
0.不带方法的样子
public unsafe class Linear<Data> where Data : unmanaged
{
Data* head;
readonly int byteSize; //Data的size
readonly int baseSize; //初始容量
int size; //当前容量
public int Length { get; private set; }
}
1.索引器和内存开辟
public Data this[int index]
{
get
{
if(!IsInLength(index))
{
throw new OverflowException("访问越界!");
}
return head[index];
}
private set
{
EnsureCapacity(ref index);
head[index] = value;
}
}
//检查容量,若不足再开辟
void EnsureCapacity(ref int index)
{
if (index < size) return;
while (index >= size)
{
size += baseSize;
GC.AddMemoryPressure(byteSize * baseSize);
}
Extend();
}
void Extend()
{
head = (Data*)Marshal.ReAllocHGlobal((IntPtr)head, new IntPtr(size * byteSize));
}
2.创建、删除与清空(连标题都抄过来)
public Linear(int n)
{
byteSize = (byte)sizeof(Data);
head = (Data*)Marshal.AllocCoTaskMem(byteSize * n);
if (head == null)
{
throw new Exception("初始化分配内存出错啦!");
}
GC.AddMemoryPressure(byteSize * n);
size = baseSize = n;
Length = 0;
}
~Linear()
{
//Linear<Data>本身还是托管的
Destroy();
}
private void Destroy()
{
if (head != null)
{
Marshal.FreeHGlobal((IntPtr)head);
GC.RemoveMemoryPressure(byteSize * size);
Length = 0;
size = 0;
}
}
public void Clear()
{
Length = 0;
}
3.各种访问
public void ListTraverse()
{
for (int i = 0; i < Length; i++)
{
Debug.Log($"{i}:{head[i]}");
}
}
public bool IsListEmpty() => Length == 0;
public Data GetElem(int pos)
{
IsInLength(pos, () => throw new OverflowException("访问越界!"));
return head[pos];
}
public int? LocateElem(Data e,int start,int end)
{
for (int i = start; i < end; i++)
{
//无法直接用 == 来判断Data类型实例之间是否相等
//不清楚Equals和ReferenceEquals是否适用(懒得测试了)
Hash128 hash1 = new Hash128();
Hash128 hash2 = new Hash128();
HashUnsafeUtilities.ComputeHash128(&head[i], (ulong)byteSize, &hash1);
HashUnsafeUtilities.ComputeHash128(&e, (ulong)byteSize, &hash2);
if (Equals(hash1, hash2))
{
return i;
}
}
return null;
}
public int? LocateElem(Data e)
{
return LocateElem(e, 0, Length);
}
public Data PriorElem(Data cur)
{
int? result = LocateElem(cur, 1, Length);
if(result is null)
{
throw new NullReferenceException("fail in PriorElem");
}
return head[(int)result-1];
}
public Data NextElem(Data cur)
{
int? result = LocateElem(cur, 0 ,Length - 1);
if (result is null)
{
throw new NullReferenceException("fail in NextElem");
}
return head[(int)result+1];
}
private bool IsInLength(int pos, Action action = null)
{
if (pos >= 0 && pos < Length)
{
return true;
}
action.Invoke();
return false;
}
public bool IsInLength(int pos)
{
return IsInLength(pos, null);
}
4.修改、插入与删除元素
public void SetElem(int pos,Data e)
{
IsInLength(pos, () => throw new OverflowException("设置越界!"));
head[pos] = e;
}
public bool InsertElem(int pos, Data e)
{
if(pos<0||pos>Length)
{
Debug.LogWarning("插入越界!");
return false;
}
for (int j = Length - 1; j >= pos; j--)
{
head[j + 1] = head[j];//向后拷贝,节约时间
}
head[pos] = e;
++Length;
return true;
}
public bool DeleteElem(int pos)//删除指定位置上的元素并返回
{
if (!IsInLength(pos, () => Debug.LogWarning("删除越界!")))
{
return false;
}
for (int j = pos + 1; j < Length; j++)
{
head[j - 1] = head[j];
}
--Length;
return true;
}
5.测试和输出(unity环境)
int类型
void Start()
{
Linear<int> li = new Linear<int>(5);
li.InsertElem(0, 3);
li.InsertElem(1, 8);
li.InsertElem(2, 42);
li.Clear();
li.InsertElem(0, 7);
li.InsertElem(1, 5);
li.InsertElem(2, 4);
li.InsertElem(3, 12);
li.InsertElem(4, 91);
li.ListTraverse();
Debug.LogError(li.LocateElem(4));
Debug.LogError(li.NextElem(7));
}
0:7
1:5
2:4
3:12
4:91
2
5
Vector3类型
void Start()
{
Linear<Vector3> li2 = new Linear<Vector3>(5);
li2.InsertElem(0, Vector3.forward);
li2.InsertElem(1, Vector3.right);
li2.InsertElem(2, Vector3.back);
li2.InsertElem(3, Vector3.down);
li2.DeleteElem(2);
li2.ListTraverse();
Debug.LogError(li2.LocateElem(Vector3.right));
}
0:(0.00, 0.00, 1.00)
1:(1.00, 0.00, 0.00)
2:(0.00, -1.00, 0.00)
1
差不多就这些了,等柒氿那边更新了我再抄过来。
© 版权声明
文章版权归作者所有
THE END





- 最新
- 最热
只看作者