AndanteSoft.ArrayPoolCollection 1.1.0

dotnet add package AndanteSoft.ArrayPoolCollection --version 1.1.0                
NuGet\Install-Package AndanteSoft.ArrayPoolCollection -Version 1.1.0                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="AndanteSoft.ArrayPoolCollection" Version="1.1.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add AndanteSoft.ArrayPoolCollection --version 1.1.0                
#r "nuget: AndanteSoft.ArrayPoolCollection, 1.1.0"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install AndanteSoft.ArrayPoolCollection as a Cake Addin
#addin nuget:?package=AndanteSoft.ArrayPoolCollection&version=1.1.0

// Install AndanteSoft.ArrayPoolCollection as a Cake Tool
#tool nuget:?package=AndanteSoft.ArrayPoolCollection&version=1.1.0                

ArrayPoolCollection

A low-allocation collection library using pooled arrays

<a href="https://www.nuget.org/packages/AndanteSoft.ArrayPoolCollection">NuGet Version</a> <a href="LICENSE">GitHub License</a> <a href="https://www.nuget.org/packages/AndanteSoft.ArrayPoolCollection">NuGet Downloads</a>

Logo

Basic Usage

// By using the `using` statement, it will be automatically returned to the pool.
using var dict = new ArrayPoolDictionary<int, string>();

// Can be used in the same way as a Dictionary
dict.Add(123, "Alice");
Console.WriteLine(dict[123]);   // "Alice"

Install

ArrayPoolCollection can be installed from NuGet AndanteSoft.ArrayPoolCollection.

dotnet add package AndanteSoft.ArrayPoolCollection

ArrayPoolCollection requires .NET Standard 2.1 or .NET 9. (There are performance benefits to using .NET 9 where possible.)

If you want to use it with MemoryPack, install AndanteSoft.ArrayPoolCollection.MemoryPack instead.

Unity

Supported version: 2021.2 or later. (API Compatibility Level: .NET Standard 2.1)

My test environment is 6000.1.0b1.

Use NuGetForUnity to install.

Features

It provides the following collections, which are mostly compatible with the default collections:

They use ArrayPool<T> for their internal arrays, and by calling Dispose() when destroyed, they do not generate GC garbage. These implement almost all APIs available as of .NET 9.

Therefore, it can also be used to utilize PriorityQueue, which was not available in the current Unity environment.

Example of Dictionary usage:

using var dict = new ArrayPoolDictionary<string, string>(new Utf8BytesComparer())
{
    { "Alice", "Supernova" }
};

// You can use GetAlternateLookup in Unity.
// Unfortunately, it is not a `allows ref struct` constraint, so
// `ReadOnlySpan<byte>` cannot be taken as an argument.
Debug.Log(dict["Alice"]);   // "Supernova"
Debug.Log(dict.GetAlternateLookup<byte[]>()[Encoding.UTF8.GetBytes("Alice")]);  // "Supernova"

private class Utf8BytesComparer : IEqualityComparer<string>, IAlternateEqualityComparer<byte[], string>
{
    public string Create(byte[] alternate)
    {
        return Encoding.UTF8.GetString(alternate);
    }

    public bool Equals(byte[] alternate, string other)
    {
        return Encoding.UTF8.GetBytes(other).SequenceEqual(alternate);
    }

    public bool Equals(string x, string y)
    {
        return x.Equals(y);
    }

    public int GetHashCode(byte[] alternate)
    {
        return Encoding.UTF8.GetString(alternate).GetHashCode();
    }

    public int GetHashCode(string obj)
    {
        return obj.GetHashCode();
    }
}

In addition, it implements the following alternative collections, although they have some differences in specifications:

Reason: It's an old collection and the design is old. For example, And and Xor returns void ​​to indicate that they are mutable. Also, BitArray only implements ICollection (non-generic!), while ArrayPoolBits implements IList<bool>.

Also implement an object pool:

  • ObjectPool

By utilizing this and reusing instances of ArrayPool***, GC garbage can be completely eliminated. This is also available in the pool of GameObjects in Unity.

// Get a dictionary from the pool
var dict = ObjectPool<ArrayPoolDictionary<int, string>>.Shared.Rent();

// The contents of the rented dictionary are undefined, so clear it.
dict.Clear();

// Then use it just like a normal Dictionary.
dict.Add(123, "Alice");
dict.Add(456, "Barbara");
dict.Add(789, "Charlotte");

Debug.Log($"{dict[123]}");      // "Alice"

// Return it when you're done using it
ObjectPool<ArrayPoolDictionary<int, string>>.Shared.Return(dict);

Example of pooling GameObject in Unity:

var root = new GameObject("root");

// Create GameObjectPool
using var gameObjectPool = new ObjectPool<GameObject>(PooledObjectCallback<GameObject>.Create(
    // OnInstantiate - create prefab
    () =>
    {
        var go = GameObject.CreatePrimitive(PrimitiveType.Cube);
        go.transform.SetParent(root.transform, false);
        return go;
    },
    // OnRent - set active
    go => go.SetActive(true),
    // OnReturn - set inactive
    go => go.SetActive(false),
    // OnDestroy - destroy
    go => Destroy(go)
));

// Generate a specified number of pieces in advance
gameObjectPool.Prewarm(16);

for (int i = 0; i < 64; i++)
{
    // Rent & Return
    var go = gameObjectPool.Rent();
    go.transform.position = UnityEngine.Random.insideUnitSphere * 10;
    UniTask.Delay(1000).ContinueWith(() => gameObjectPool.Return(go)).Forget();

    await UniTask.Delay(50);
}

await UniTask.Delay(1000);

It also includes an efficient implementation of IBufferWriter<T> that utilizes a pool.

  • ArrayPoolBufferWriter<T> : Suitable for small or fixed size buffers.
  • ArrayPoolSegmentedBufferWriter<T> : Suitable for objects of variable length and larger than megabytes.

For usage, see the With MemoryPack section.

With MemoryPack

Collections can be serialized using MemoryPack.

// Call this once at the beginning
ArrayPoolCollectionRegisterer.Register();

using var list = new ArrayPoolList<string>()
{
    "Alice", "Barbara", "Charlotte",
};

// Create IBufferWriter<byte>
using var writer = new ArrayPoolBufferWriter<byte>();

// Serialize the list
MemoryPackSerializer.Serialize(writer, list);

// Deserialize the list
var deserialized = MemoryPackSerializer.Deserialize<ArrayPoolList<string>>(writer.WrittenSpan);

// "Alice, Barbara, Charlotte"
Debug.Log(string.Join(", ", deserialized));

Notes:

  • ArrayPoolPriorityQueue do not implement IEnumerable<T>, so serialization requires the [MemoryPackAllowSerialize] attribute.
  • Comparer of Dictionary/HashSet is not usually serializable, so it is not saved. If you want to save it, you need to overwrite it when serializing.
using var ignoreCase = new ArrayPoolDictionary<string, int>(StringComparer.OrdinalIgnoreCase)
{
    { "Alice", 16 },
};

byte[] bytes = MemoryPackSerializer.Serialize(ignoreCase);

// ---

var deserialized = new ArrayPoolDictionary<string, int>(StringComparer.OrdinalIgnoreCase);
MemoryPackSerializer.Deserialize(bytes, ref deserialized);

Debug.Log($"{deserialized["alice"]}");      // 16

Fork

Build

dotnet build

Run tests

dotnet test

Run benchmarks

dotnet run -c Release --project ArrayPoolCollection.Benchmarks

Publish

dotnet pack

License

MIT License

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on AndanteSoft.ArrayPoolCollection:

Package Downloads
AndanteSoft.ArrayPoolCollection.MemoryPack

A low-allocation collection library using pooled arrays (with MemoryPack)

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.1.0 47 2/14/2025
1.0.0 81 2/10/2025