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


111

.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. 私が現在書いているのは、より大きなフレームワークの一部としてデフォルトの機能を提供する基本クラスです。私が提供するデフォルトの機能では、リストのサイズは事前にわかっているため、配列を使用することもできます。ただし、どの基本クラスにも動的に拡張する機会を提供したいので、リストを選びます。


1
「編集:これまでの回答は、容量を参照しています。これはリストに事前入力する以外の方法です。たとえば、容量10で作成したばかりのリストでは、L [2] = "somevalue"を実行できません」変更、おそらくあなたは質問のタイトルを書き直す必要があります...
アラナサウルス2009年

しかし、リストに空の値を事前に設定することは何ですか?それは、トピックスターターが何をしようとしているのですか?
Frederik Gheysels 2009年

1
位置マッピングが非常に重要な場合は、Dictionary <int、string>を使用する方が理にかなっていますか?
グレッグD

7
Listはの代替品ではありませんArray。彼らは明確に別の問題を解決します。あなたは、固定サイズが必要な場合は、したいですArray。を使用する場合List、それは間違っています。
Zenexer 2013年

5
「なぜ必要になるのかわからない…」といった議論をぶつけようとする答えをいつも見つけだします。それはただそれだけを意味します:あなたはそれを見ることができませんでした。それは必ずしも何も意味しない。私は人々が問題へのより良いアプローチを提案したいと思っていることを尊重しますが、それはもっと謙虚に表現されるべきです。この方法は、それが魅力、快適になり、そしてその疑問を改善するためのOPを奨励しています。勝者になる-謙虚になる。
AnorZaken

回答:


79

これが頻繁に必要であるとは言えません。なぜこれが必要なのか、詳細を教えてください。私はおそらくそれをヘルパークラスの静的メソッドとして置きます:

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;
    }
}

使用することもできますEnumerable.Repeat(default(T), count).ToList()が、バッファのサイズ変更により効率が悪くなります。

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

編集:コメントで述べたように、必要にRepeated応じてループを使用してリストにデータを追加できます。それも少し速くなります。個人的に私はRepeatより記述的なコードを使用しているコードを見つけ、現実の世界ではパフォーマンスの違いは無関係であると思われますが、実際の距離は異なる場合があります。


1
これは古い投稿だと思いますが、気になります。Enumerable.Repeatは、リンクの最後の部分(dotnetperls.com/initialize-array)のように、forループに比べてはるかに劣ります。また、AddRange()は、msdnに従ってO(n)の複雑さを持っています。単純なループの代わりに指定されたソリューションを使用することは、少しカウンター生産的ではありませんか?
ジミー

@Jimmy:どちらのアプローチもO(n)になります。このアプローチは、私が達成しようとしていることをより詳しく説明していると思います。ループが必要な場合は、自由に使用してください。
Jon Skeet

@Jimmy:また、ベンチマークがを使用していることにも注意してくださいEnumerable.Repeat(...).ToArray()。これは、私が使用している方法ではありません
Jon Skeet、2014年

1
私はEnumerable.Repeat()次のように使用しました(PairC ++と同じように実装されています)。Enumerable.Repeat( new Pair<int,int>(int.MaxValue,-1), costs.Count)副作用Listが、単一のオブジェクトへの参照コピーでいっぱいであることを通知しています。要素を変更するmyList[i].First = 1と、全体のすべての要素が変更されListます。このバグを見つけるのに何時間もかかりました。あなたたちはただ、共通のループと使用を使用することを除いて(この問題への解決策を知っていますか.Add(new Pair...)
00zetti

1
@ Pac0、以下に回答を追加しました。編集は承認されない場合があります。
ジェレミーレイブラウン

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

3
個人的にはこれが最もクリーンな方法だと思います-質問本文では潜在的に不器用であると述べられていましたが
hello_earth

4
+1:インデックスを介して入力する前に、固定サイズのnullで変数サイズリストを初期化する必要がある状況に遭遇しました(その後、追加で追加するため、配列は不適切でした)。この答えは、実用的でシンプルであるという私の投票を獲得します。
コーディング終了

すばらしい答え。@GoneCoding内部的に新しく初期化されたリストLがそのままのメモリを再利用するかstring[10](リストのバッキングストアも配列です)、それとも独自の新しいメモリを割り当ててからstring[10]?後のルートを選択するstring[10]と、ガベージコレクションが自動的に行われますL
RBT 2017年

3
@RBTこれがこのアプローチの唯一の欠点です。配列は再利用されないため、一時的に配列の2つのコピーが作成されます(リストは内部的に配列を使用しているようです)。まれに、非常に多くの要素やメモリの制約がある場合に問題になることがあります。そしてはい、追加の配列は、Listコンストラクターが完了するとすぐにガベージコレクションの対象になります(それ以外の場合は、恐ろしいメモリリークになります)。適格とは、「すぐに収集される」という意味ではなく、次回ガベージコレクションが実行されるときを意味します。
AnorZaken 2017年

2
この答えは、10個の新しい文字列を割り当て、必要に応じてそれらをコピーしてそれらを反復します。大規模な配列で作業している場合。受け入れられた回答の2倍のメモリを必要とするため、これを考慮しないでください。
UKMonkey

22

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

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

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

EDIT2:

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

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


46
この回答は、リストに10個のnullエントリを割り当てません(これは要件でした)。リストのサイズ変更が必要になる前に、 10個のエントリスペースを割り当てます(つまり、容量)のでnew List<string>()、問題が発生する限り、何も変わりません。。しかし、非常に多くの賛成投票を獲得することはできました:)
コーディング終了

2
そのオーバーロードされたコンストラクタは、「サイズ」や「長さ」ではなく「初期容量」の値であり、アイテムも初期化しません
Matt Wilko

「なぜ誰かがこれを必要とするのか」に答えるには、ディープクローニングツリーのデータ構造に今すぐ必要です。ノードは、その子を生成する場合としない場合がありますが、ベースノードクラスは、そのすべての子ノードでそれ自体を複製できる必要があります。なんてことだ、もっともっと複雑だ。しかし、私はまだ空のリストを介してデータlist[index] = obj;を入力することと、他のいくつかのリスト機能を使用することの両方が必要です。
Bitterblue

3
@Bitterblue:また、すべての値が前にない場合がある、事前に割り当てられたあらゆる種類のマッピング。確かに用途があります。私は経験の少ない日にこの答えを書きました。
Ed S.

8

最初に必要な項目の数で配列を作成し、次に配列をリストに変換します。

int[] fakeArray = new int[10];

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

7

固定値で初期化したいのに、なぜリストを使用しているのですか?パフォーマンスのために、初期容量を指定したいのですが、必要なときに拡張できる通常の配列よりもリストの利点の1つではありませんか?

これを行うと:

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

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


1
「固定値で初期化したいのに、なぜリストを使用するのですか」良い点です。
Ed S.

7
彼は、各要素が初期化され、リストに容量だけでなくサイズがあるリストを要求しました。現在のところ、この答えは正しくありません。
Nic Foster

質問には答えが必要であり、尋ねられたという批判はありません。配列を使用するのではなく、この方法で実行する理由がある場合があります。なぜこれが当てはまるのか興味がある場合は、スタックオーバーフローの質問をすることができます。
Dino Dini

5

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


これは質問には答えませんが、質問されない理由を与えるだけです。
Dino Dini

5

データとの位置的な関連付けの必要性を強調しているようですが、連想配列の方が適しているのではないでしょうか。

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

これは、尋ねられている質問とは異なる質問への回答です。
Dino Dini

4

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

public List<T> InitList<T>(int count, T initValue)
{
  return Enumerable.Repeat(initValue, count).ToList();
}

この手法によるバッファのサイズ変更に関する懸念については、John Skeetの回答を参照してください。
エイミーB

1

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

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

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

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

1
string [] temp = new string[] {"1","2","3"};
List<string> temp2 = temp.ToList();

List <string> temp2 = new List <string>(temp);はどうですか?すでに提案されているOPのように。
Ed S.

Ed-これは実際に質問に答えます-これは容量についてではありません。
ボアズ

しかしOPは、彼がこの解決策を好まなかったとすでに述べています。
Ed S.

1

承認された回答(緑色のチェックマークが付いている回答)には問題があります。

問題:

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
これは「問題」ではなく、参照をコピーする通常の動作です。状況を知らなければ、オブジェクトをコピーするのが適切かどうかはわかりません。問題は、リストにコピーを入力する必要があるかどうかではなく、容量でリストを初期化することです。私はそれが行動の面で私の答えを明確によありますが、私は本当にそれがこの質問の文脈で問題としてカウントされないと思います。
Jon Skeet、

@JonSkeet、私は静的ヘルパーに返信していました。これはプリミティブ型では大丈夫ですが、参照型では大丈夫です。同じ参照アイテムでリストが初期化されるシナリオは正しくないようです。そのシナリオでは、リスト内のすべてのアイテムがヒープ上の同じオブジェクトを指している場合でも、リストが存在するのはなぜですか。
ジェレミーレイブラウン

サンプルシナリオ:最初にすべての要素の「不明」のエントリで文字列のリストを入力し、次に特定の要素のリストを変更します。その場合、すべての「不明」値が同じ文字列への参照であることは完全に合理的です。毎回文字列を複製しても意味がありません。同じオブジェクトへの複数の参照を文字列に設定することは、一般的な意味で「正しい」または「間違った」ことではありません。読者がその振る舞いを理解している限り、それが特定のユースケースを満たすかどうかを判断するのは読者次第です。
ジョンスキート

0

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

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


0

これは、単体テストで使用したサンプルです。クラスオブジェクトのリストを作成しました。次に、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);


    }

皆さんのお役に立てば幸いです。


-1

あなたが提案した少し遅いが最初の解決策は私にははるかにきれいに見えます:メモリを2回割り当てることはありません。リストのコンストラクタでさえ、コピーするために配列をループする必要があります。事前にnull要素のみが存在することもわかりません。

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

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.