4
3

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-27

注意

前回のコードはref構造体なので,絶対ヒープに乗らず,リストを使用するよりもパフォーマンスが優れているはずですが,今回のコードは微妙です.
ただなんとなく「最大サイズが決まってるリストにリストを使いたくない」という気持ちで書いたもので,パフォーマンスについて検証してません.

使用例

バッファ用の構造体を用意し,インターフェースを継承してAsSpanを公開する.
inlineArrayが使える場合はスマートに書けます.
(※コンストラクタがグロテスクになるのでstatic関数でリストを作成するとよいかも)

public struct Buffer4<T> : IStructBuffer<T> // Tが4つ入るバッファ
{
    public T t0, t1, t2, t3;
    public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref t0, 4);
    public static FixedList<T, Buffer4<T>> List() => new FixedList<T, Buffer4<T>>();
}
public struct Buffer16<T> : IStructBuffer<T> // Tが16個入るバッファ
{
    public Buffer4<T> t0, t1, t2, t3;
    public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref t0.t0, 16);
    public static FixedList<T, Buffer16<T>> List() => new FixedList<T, Buffer16<T>>();
}

var list4 = Buffer4<int>.List();
list4.Add(1);
list4.Add(2);
list4.Add(1);
list4.Add(1);
list4.RemoveRange(1, 2);
list4.Insert(1, 3);
foreach(var i in list4.AsSpan())
{
    Console.WriteLine(i); // 1,3,1
}

コード

public interface IStructBuffer<T>
{
    Span<T> AsSpan();
}
public struct FixedList<T,BufferT> where BufferT:IStructBuffer<T>
{
    private int count;
    private BufferT buffer;

    public Span<T> AsSpan() => buffer.AsSpan().Slice(0, count);
    public int Count => count;
    public bool ContainsIndex(int i) => 0 <= i && i < count;

    // Edit Method
    public void Clear() => count = 0;
    public void Add(in T add)
    {
        var span = buffer.AsSpan();
        if (span.Length < count + 1) throw new IndexOutOfRangeException("確保バッファを超過");
        span[count++] = add;
    }
    public void RemoveRange(int start, int range)
    {
        if (!ContainsIndex(start)) throw new IndexOutOfRangeException("削除開始インデックスがリスト外");
        if (!ContainsIndex(start + range - 1)) throw new IndexOutOfRangeException("削除開始インデックスから指定範囲を行くとはみ出ます");
        count -= range;
        var span = buffer.AsSpan();
        int backCount = span.Length - start - range;
        Span<T> copyFrom = span.Slice(start + range, backCount);// 削除範囲含まない後ろの全部
        Span<T> copyTo = span.Slice(start, backCount);// 削除開始を含む後ろ
        copyFrom.CopyTo(copyTo);
        for(int i = 0; i < range; i++)
        {
            span[count + i] = default;
        }
    }
    public void Insert(int index, in T insert)
    {
        int add = 1;
        int newCount = count + add;
        var span = buffer.AsSpan();
        int bufferCapacity = span.Length;
        if (bufferCapacity < newCount) throw new IndexOutOfRangeException();
        count = newCount;

        int backCount = bufferCapacity - index - add;
        Span<T> copyFrom = span.Slice(index, backCount);
        Span<T> copyTo = span.Slice(index + add, backCount);
        copyFrom.CopyTo(copyTo);
        span[index] = insert;
    }
    public void Insert(int index, in T t0, in T t1, in T t2)
    {
        int add = 3;
        int newCount = count + add;
        var span = buffer.AsSpan();
        int bufferCapacity = span.Length;
        if (bufferCapacity < newCount) throw new IndexOutOfRangeException();
        count = newCount;

        int backCount = bufferCapacity - index - add;
        Span<T> copyFrom = span.Slice(index, backCount);
        Span<T> copyTo = span.Slice(index + add, backCount);
        copyFrom.CopyTo(copyTo);
        span[index++] = t0;
        span[index++] = t1;
        span[index] = t2;
    }
}
4
3
0

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
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?