web-dev-qa-db-ja.com

List <T>を(容量ではなく)特定のサイズに初期化する方法は?

.NETは、パフォーマンスがほぼ同じである汎用リストコンテナーを提供します(配列とリストのパフォーマンスの質問を参照)。ただし、初期化はまったく異なります。

配列はデフォルト値で非常に簡単に初期化でき、定義により既に一定のサイズになっています。

string[] Ar = new string[10];

これにより、ランダムなアイテムを安全に割り当てることができます:

Ar[5]="hello";

リストを使用することはよりトリッキーです。同じ初期化を行う2つの方法がありますが、どちらもエレガントとは言いません。

List<string> L = new List<string>(10);
for (int i=0;i<10;i++) L.Add(null);

または

string[] Ar = new string[10];
List<string> L = new List<string>(Ar);

よりクリーンな方法は何でしょうか?

編集:これまでの答えは容量に関するもので、これはリストに事前入力する以外のことです。たとえば、容量10で作成されたばかりのリストでは、L[2]="somevalue"を実行できません

編集2:人々はなぜリストをこのように使用したいのか疑問に思っています。次の2つの理由がわかります。

  1. リストは「次世代」の配列であり、ほとんどペナルティのない柔軟性を追加していると非常に説得力を持って主張できます。したがって、デフォルトでそれらを使用する必要があります。初期化はそれほど簡単ではないかもしれないと指摘しています。

  2. 私が現在書いているのは、より大きなフレームワークの一部としてデフォルト機能を提供する基本クラスです。私が提供するデフォルトの機能では、リストのサイズは高度に知られているため、配列を使用できます。ただし、基本クラスに動的に拡張する機会を提供したいので、リストを選択します。

96
Boaz

頻繁にこれが必要だとは言えません-なぜこれが必要なのかを詳しく教えてもらえますか?私はおそらくヘルパークラスの静的メソッドとしてそれを入れたでしょう:

public static class Lists
{
    public static List<T> RepeatedDefault<T>(int count)
    {
        return Repeated(default(T), count);
    }

    public static List<T> Repeated<T>(T value, int count)
    {
        List<T> ret = new List<T>(count);
        ret.AddRange(Enumerable.Repeat(value, count));
        return ret;
    }
}

couldEnumerable.Repeat(default(T), count).ToList()を使用しますが、バッファーのサイズ変更のために非効率的です。

Tが参照型である場合、countパラメーターに渡された参照のvalueコピーを格納することに注意してください。したがって、それらはすべて同じオブジェクトを参照します。それは、ユースケースに応じて、必要な場合とそうでない場合があります。

編集:コメントに記載されているように、必要に応じて、Repeatedでループを使用してリストを作成できます。それも少し速くなります。個人的にはRepeatを使用したコードの方がわかりやすく、実際の世界ではパフォーマンスの違いは関係ないと思われますが、走行距離は異なる場合があります。

72
Jon Skeet
List<string> L = new List<string> ( new string[10] );
112
NewBe

引数としてint(「容量」)を取るコンストラクターを使用します。

List<string> = new List<string>(10);

編集:私はフレデリクに同意することを追加する必要があります。そもそもリストを使用する背後にある理由全体に反する方法でリストを使用しています。

EDIT2:

編集2:私が現在書いているのは、より大きなフレームワークの一部としてデフォルト機能を提供する基本クラスです。私が提供するデフォルトの機能では、リストのサイズは高度に知られているため、配列を使用できます。ただし、基本クラスに動的に拡張する機会を提供したいので、リストを選択します。

なぜ誰もがすべてのnull値を持つリストのサイズを知る必要があるのですか?リストに実際の値がない場合、長さは0になると予想されます。とにかく、これが不格好であるという事実は、クラスの意図された使用に反していることを示しています。

24
Ed S.

固定値で初期化するのにリストを使用するのはなぜですか? (パフォーマンスのために)初期容量を与えたいが、通常の配列に対するリストの利点の1つではなく、必要に応じて拡張できることを理解できますか?

これを行うとき:

List<int> = new List<int>(100);

容量が100整数のリストを作成します。つまり、101番目のアイテムを追加するまで、リストを「成長」させる必要はありません。リストの基になる配列は長さ100で初期化されます。

7

そのようなリストの内容を初期化することは、実際にはリストの目的ではありません。リストはオブジェクトを保持するように設計されています。特定の数値を特定のオブジェクトにマッピングする場合は、リストではなく、ハッシュテーブルや辞書のようなキーと値のペア構造の使用を検討してください。

5
Welbog

最初に必要な数のアイテムを含む配列を作成してから、配列をリストに変換します。

int[] fakeArray = new int[10];

List<int> list = fakeArray.ToList();
5
mini998

データとの位置の関連付けの必要性を強調しているようですが、連想配列の方が適していませんか?

Dictionary<int, string> foo = new Dictionary<int, string>();
foo[2] = "string";
4
Greg D

固定値のN個の要素でリストを初期化する場合:

public List<T> InitList<T>(int count, T initValue)
{
  return Enumerable.Repeat(initValue, count).ToList();
}
4
Amy B
string [] temp = new string[] {"1","2","3"};
List<string> temp2 = temp.ToList();
1
Henk

受け入れられた回答(緑色のチェックマークが付いているもの)には問題があります。

問題:

var result = Lists.Repeated(new MyType(), sizeOfList);
// each item in the list references the same MyType() object
// if you edit item 1 in the list, you are also editing item 2 in the list

オブジェクトのコピーを実行するには、上記の行を変更することをお勧めします。それについて多くの異なる記事があります:

リスト内のすべてのアイテムをNULLではなくデフォルトのコンストラクターで初期化する場合は、次のメソッドを追加します。

public static List<T> RepeatedDefaultInstance<T>(int count)
    {
        List<T> ret = new List<T>(count);
        for (var i = 0; i < count; i++)
        {
            ret.Add((T)Activator.CreateInstance(typeof(T)));
        }
        return ret;
    }
1

IListに関する通知: MSDN IList Remarks : "IListの実装は、読み取り専用、固定サイズ、可変サイズの3つのカテゴリに分類されます。(...)。このインターフェイスの汎用バージョンの場合、System.Collections.Generic.IList<T>を参照してください。」

IList<T>IListから継承しません(ただし、List<T>IList<T>IListの両方を実装しますが、常に 可変サイズ です。 。 .NET 4.5以降、IReadOnlyList<T>もありますが、私たちが探しているものとなる固定サイズの汎用リストはありません。

0
EricBDev

Linqを使用して、リストをデフォルト値で巧妙に初期化できます。 ( David Bの答え に似ています。)

var defaultStrings = (new int[10]).Select(x => "my value").ToList();

さらに一歩進んで、各文字列を個別の値「string 1」、「string 2」、「string 3」などで初期化します。

int x = 1;
var numberedStrings = (new int[10]).Select(x => "string " + x++).ToList();
0
James Lawruk

これは、ユニットテストに使用したサンプルです。クラスオブジェクトのリストを作成しました。次に、forloopを使用して、サービスに期待する「X」個のオブジェクトを追加しました。この方法で、任意のサイズのリストを追加/初期化できます。

public void TestMethod1()
    {
        var expected = new List<DotaViewer.Interface.DotaHero>();
        for (int i = 0; i < 22; i++)//You add empty initialization here
        {
            var temp = new DotaViewer.Interface.DotaHero();
            expected.Add(temp);
        }
        var nw = new DotaHeroCsvService();
        var items = nw.GetHero();

        CollectionAssert.AreEqual(expected,items);


    }

皆さんの助けになればと思います。

0
Abhay Shiro

あなたが提案した少し遅いが最初の解決策は私にははるかにきれいです:メモリを2回割り当ててはいけません。 List constrcutorでさえ、配列をループする必要があります。事前にnull要素しか含まれていないことも事前にはわかりません。

1.-Nを割り当てる-ループNコスト:1 * allocate(N)+ N * loop_iteration

2.-割り当てN-割り当てN +ループ()コスト:2 * allocate(N)+ N * loop_iteration

ただし、Listは組み込みクラスであるため、Listの割り当てとループは高速になる可能性がありますが、C#はjitコンパイル済みです...

0
Victor Drouin