LoginSignup
7
5

More than 5 years have passed since last update.

C# のコレクション使用法のガイドライン その1 配列

Posted at

今日は、.NET のクラスライブラリ設計の本を読んだ。Collection 周りが理解できていないのがわかったので、設計方針をリストアップして、分かっていないところをつぶしたいと思う。

配列

DO: パブリックなAPIにおいては、配列よりもコレクションの使用を選択する


public Collection<Item> Items { get; set; }

コレクションのほうが、高いユーザビリティを誇るため。ただし、低レベルのAPIを開発しているときは、配列を使用するほうが良いときがある。配列の要素のアクセスは、コレクションより高速であるから。メモリを抑えて高速化したいときなど。

また、バイトの場合は、コレクションより配列。(これは上記の理由と思われる。)

public byte[] ReadBytes() {...

多次元配列の代わりに、ジャグ配列を使用する

ジャグ配列というのは、配列の要素が配列であるもの。多次元配列と比較すると、メモリ消費量が有利で、CLRは、ジャグ配列におけるインデックス操作も最適化してくれるので、早そうです。これは、本当かしらんので試してみましょう。

メモリがどう違うか確かめてみます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CollectionSpike
{

    class NewInt
    {
        public int Value { get; private set; }
        public NewInt(int value)
        {
            Value = value;
        }
    }
    class OldInt
    {
        public int Value { get; private set; }
        public OldInt(int value)
        {
            Value = value;
        }
    }
    class ArraySpike
    {
        public void Exec()
        {
            OldInt[][] jaggedArray =
            {
                new OldInt[] {new OldInt(1), new OldInt(2), new OldInt(3), new OldInt(4)},
                new OldInt[] {new OldInt(5), new OldInt(6), new OldInt(7)},
                new OldInt[] {new OldInt(8)},
                new OldInt[] {new OldInt(9)}
            };

            NewInt[,] multiDimArray =
            {
                {new NewInt(1), new NewInt(2), new NewInt(3), new NewInt(4)},
                {new NewInt(5), new NewInt(6), new NewInt(7), new NewInt(0)},
                {new NewInt(8), new NewInt(0), new NewInt(0), new NewInt(0)},
                {new NewInt(9), new NewInt(0), new NewInt(0), new NewInt(0)}
            };

            var sw = new System.Diagnostics.Stopwatch();

            var times = 10000000;

            sw.Start();
            for (int i = 0; i < times; i++)
            {
                foreach (var items in jaggedArray)
                {

                    foreach (var item in items)
                    {
                        if (item.Value == 8)
                        {

                        }
                    }
                }
            }
            sw.Stop();
           Console.WriteLine($"jagged.foreach.foreach: {sw.Elapsed}");

            sw.Restart();
            for (int i = 0; i < times; i++)
            {
                jaggedArray.SelectMany(x => x).Select(x => x.Value % 2 == 0);
            }
            sw.Stop();
            Console.WriteLine($"jagged.SelectMany.Select: {sw.Elapsed}");

            sw.Restart();
            for (int i = 0; i < times; i++)
            {
                foreach (var item in multiDimArray)
                {
                    if (item.Value == 8)
                    {

                    }
                }
            }

            sw.Stop();
            Console.WriteLine($"multiDim foreach: {sw.Elapsed}");

            Console.WriteLine($"jagged: {jaggedArray} multi: {multiDimArray}");
        }
    }
}

実行結果

jagged.foreach.foreach: 00:00:03.2893552
jagged.SelectMany.Select: 00:00:01.8337845
multiDim foreach: 00:00:09.8060043

ん、確かに。しかし、Linq が一番早いのは意外!優秀ですね。ちなみに、多次元配列だと、Linqはつかえませんでした。
じゃあ、メモリ。Visual Studio で、ヒープのスナップショットを撮って、比較してみます。ジャグ配列は単純に、ある型としてヒープが消費されていますが、多次元配列は、新たに多次元配列の型が出来てメモリを消費しているようです。たしかに、倍ぐらい多次元配列のほうがメモリを食っています。今はLinq もあるし、確かにジャグ配列ですな。

memory.png

長くなったので、まずは第一弾ここまで。

7
5
2

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