8
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

c#でヒープに乗らないリストっぽいものを作ってみた

Last updated at Posted at 2025-03-26

使い方

Unityで使う用の一時的な処理で使うリストを作ってみました.スタック上でadd remove insertなどができます。

完全に関数内で完結させる場合の初期化

ListedSpan<T> list = new ListedSpan<T>(stackalloc T[maxsize]);

既存のSpanをリストとして操作するときの例

// 独自の配列。例として-1の要素は空を意味することにする。もちろんこれはヒープ上にある。
int[] sampleBufferForList = new int[10]{ 124,300,452,354,-1,-1,-1,-1,-1,-1 }
int count = Test.GetCount(sampleBufferForList); // 4になることにする。

// リストで操作が簡単になる。
ListedSpan<int> list = new ListedSpan<int>(sampleBufferForList.AsSpan(),count);
list.Add(0);
// 124,300,452,354, 0 ,-1,-1,-1,-1,-1 となる
list.Clear();
// 未使用領域の管理はしない。この場合リスト全体が未使用領域になったが配列の値は変化しない。
// ClearだけでなくRemoveも未使用領域が新たに発生するので注意
// 124,300,452,354, 0 ,-1,-1,-1,-1,-1 となる
list.Add(9999);
// 9999,300,452,354, 0 ,-1,-1,-1,-1,-1 となる
list.SetUnusedValues(-1);
// 9999,-1,-1,-1,-1,-1,-1,-1,-1,-1 となる

コード

using System;
public ref struct ListedSpan<T>
{
    public int count;
    public Span<T> bufferSpan;

    public int BufferCapacity => bufferSpan.Length;

    /// <summary>
    /// = new ListedSpan<T>(stackalloc T[maxcount]);と使う
    /// </summary>
    public ListedSpan(Span<T> span,int count = 0)
    {
        bufferSpan = span;
        this.count = count;
    }
    public Span<T> ListSpan => bufferSpan.Slice(0, count);
    public ref T this[int index]
    {
        get
        {
            if(index<count)return ref bufferSpan[index];
            throw new IndexOutOfRangeException("リストの未納範囲です");
        }
    }
    public void Clear() => count = 0;
    public void Add(in T add)
    { 
        bufferSpan[count++] = add;
    }

    public void RemoveRange(int start, int range)
    {
        count -= range;
        int backCount = BufferCapacity - start - range;
        Span<T> copyFrom = bufferSpan.Slice(start + range, backCount);// 削除範囲含まない後ろの全部
        Span<T> copyTo = bufferSpan.Slice(start, backCount);// 削除開始を含む後ろ
        copyFrom.CopyTo(copyTo);
    }
    /*
    public void Insert(int index, Span<T> elements)
    {
        int add = elements.Length;
        count += add;

        int backCount = bufferSpan.Length - index - add;
        Span<T> copyFrom = bufferSpan.Slice(index, backCount);
        Span<T> copyTo = bufferSpan.Slice(index + add, backCount);
        copyFrom.CopyTo(copyTo);

        elements.CopyTo(bufferSpan.Slice(index, add));//インデックスからコピー個数分の範囲
    }*/
}
public static class ExListetSpan
{
    public static void SetUnusedValues<T>(this ListedSpan<T> list, T value)where T:struct
    {
        var span = list.bufferSpan;
        for (int i = list.count; i < span.Length; i++)
        {
            span[i] = value;
        }
    }
    public static void Insert<T>(this ListedSpan<T> list,int index,Span<T> elements)
    {
        int add = elements.Length;
        list.count += add;

        var bufferSpan = list.bufferSpan;
        int backCount = bufferSpan.Length - index - add;
        Span<T> copyFrom = bufferSpan.Slice(index, backCount);
        Span<T> copyTo = bufferSpan.Slice(index + add, backCount);
        copyFrom.CopyTo(copyTo);

        elements.CopyTo(bufferSpan.Slice(index, add));//インデックスからコピー個数分の範囲
    }
}

バッファ全体のスパンと区別するためListSpanにしました。
また、Span<T>を引数にもつ関数は拡張関数にしたほうが便利なのでInsertを拡張関数にしました。

8
7
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?