List <>の最後の要素を見つけるにはどうすればよいですか?


172

以下は私のコードからの抜粋です:

public class AllIntegerIDs 
{
    public AllIntegerIDs() 
    {            
        m_MessageID = 0;
        m_MessageType = 0;
        m_ClassID = 0;
        m_CategoryID = 0;
        m_MessageText = null;
    }

    ~AllIntegerIDs()
    {
    }

    public void SetIntegerValues (int messageID, int messagetype,
        int classID, int categoryID)
    {
        this.m_MessageID = messageID;
        this.m_MessageType = messagetype;
        this.m_ClassID = classID;
        this.m_CategoryID = categoryID;
    }

    public string m_MessageText;
    public int m_MessageID;
    public int m_MessageType;
    public int m_ClassID;
    public int m_CategoryID;
}

私は私のmain()関数コードで以下を使用しようとしています:

List<AllIntegerIDs> integerList = new List<AllIntegerIDs>();

/* some code here that is ised for following assignments*/
{
   integerList.Add(new AllIntegerIDs());
   index++;
   integerList[index].m_MessageID = (int)IntegerIDsSubstring[IntOffset];
   integerList[index].m_MessageType = (int)IntegerIDsSubstring[IntOffset + 1];
   integerList[index].m_ClassID = (int)IntegerIDsSubstring[IntOffset + 2];
   integerList[index].m_CategoryID = (int)IntegerIDsSubstring[IntOffset + 3];
   integerList[index].m_MessageText = MessageTextSubstring;
}

問題はここにあります:forループを使用してリストのすべての要素を印刷しようとしています:

for (int cnt3 = 0 ; cnt3 <= integerList.FindLastIndex ; cnt3++) //<----PROBLEM HERE
{
   Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}\n", integerList[cnt3].m_MessageID,integerList[cnt3].m_MessageType,integerList[cnt3].m_ClassID,integerList[cnt3].m_CategoryID, integerList[cnt3].m_MessageText);
}

forループでcnt3と同等にして、リストのすべてのエントリを出力できるように、最後の要素を見つけたいと思います。リストの各要素は、コードサンプルで前述したように、AllIntegerIDsクラスのオブジェクトです。リストの最後の有効なエントリを見つけるにはどうすればよいですか?

integerList.Find(integerList []。m_MessageText == null;

それを使用する場合、0から最大値までの範囲のインデックスが必要になります。使用するつもりのない別のforループを使用する必要があることを意味します。より短い/より良い方法はありますか?

ありがとう、Viren


@Viren:適切に表示するためにコードをインデントしました。私の下で編集を行った場合、私が元に戻していないことを確認できますか?
サム・ハーウェル

8
あなたの質問とは関係ありませんが、それが必要でない限り、ファイナライザを実装すべきではありません。
Brian Rasmussen、

質問とは関係ありませんが、読みやすさと保守性のために、そうすることをお勧めします。それAllIntegerIDs newItem = new AllIntegerID();を使用してすべてのフィールドを割り当て、次にを呼び出しますintegerList.Add(newItem)。または、フィールドではなくプロパティを使用し、C#3.0オブジェクト初期化構文を使用します。
ソラリン

回答:


208

リストの最後のアイテムにアクセスしたいだけなら

if(integerList.Count>0)
{
   var item = integerList[integerList.Count - 1];
}

リスト内のアイテムの総数を取得するには、Countプロパティを使用できます

var itemCount = integerList.Count;

17
@Jared最初の行の前にこの行 "if(integerList.Count!= 0)"を追加するのを忘れたと思います
prabhakaran '31年

21
私見これは最高の答えに値するものではありません。ひどく読み、countがゼロの場合はエラーが発生する可能性があります。CleanCode™アプローチは、使用することですLast/ LastOrDefault後述するように。
チリトム2013

2
以前に指摘したように、この回答はリストが空であり、IMHOを使用してはならない場合の状況を考慮していません。
merrr 2014

2
@chillitom @merrr LINQ拡張メソッドを使用しても役に立ちません。Enumerable.Lastリストが空の場合、例外がスローされます。Enumerable.LastOrDefault値タイプのリストを呼び出して渡す場合、リストが空の場合はデフォルト値が返されます。したがって、0を取得したList<int>場合、リストが空であったか、最後の値が0であったかはわかりません。つまり、Count使用するように決定した取得メカニズムを確認する必要があります。
0b101010 2014年

4
@chillitomそれぞれ独自のもの。リストにデータが入力されていることがわかっている場合var element = list[list.Count - 1]は、非常に簡潔で読みやすいと思います。拡張メソッドを呼び出す必要がない
0b101010 2014年

276

コレクションの最後の項目を取得するには、LastOrDefault()およびLast()拡張メソッドを使用します

var lastItem = integerList.LastOrDefault();

または

var lastItem = integerList.Last();

追加することを忘れusing System.Linq;ないでください。そうしないと、このメソッドは使用できなくなります。


17
はい、これが最善の方法です。LastとLastOrDefaultはList <>向けに最適化されています
チリトム

2
@Gusdor私はそれが文書化されているのを見たことはありませんが、これらのことについては、直接ソース(またはResharper、dotPeek、ILSpyなどの逆アセンブラを使用)に直接向かう傾向があります。そこから私はそれを見ることができFirstFirstOrDefaultLastLastOrDefaultSingleSingleOrDefaultElementAtおよびElementAtOrDefaultのために最適化されIList<TSource>CountそしてContains用に最適化されているICollection<TSource>Cast<TResult>のために最適化されていますIEnumerable<TResult>
チリトム2013

8
追加することを確認してくださいusing System.Linq;
ハイブリッド

4
@chillitomによって公開される拡張メソッドSystem.Linq.Enumerableは、実際には「最適化」されていません。Enumerable.Lastメソッドのコードは次のとおりです。
0b101010 2014年

4
@chillitomのソースを読んだ後System.Linq.Enumerable.Last、私は0b101010に同意します- Last()コードList<>s 用に最適化されていません」- Last()単なる醜いラッパーでありreturn list[list.Count-1]、引数がの場合のデフォルトであり、ケースの最後までリストIList反復しますそうではありません...それは非常に貧弱なソリューション作る場合IListされLinkedList、インデクサがちょうど不リスト全体を通過しますので、(私はオーバーライドが上後方反復発見していないItem[]C#のソースにインデックス>カウント/ 2と、メーリングリストへ)

20

問題の根源、リストの最後の要素に安全に対処する方法を見てみましょう...

想定

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

その後

//NOT safe on an empty list!
string myString = myList[myList.Count -1];

//equivalent to the above line when Count is 0, bad index
string otherString = myList[-1];

最初にリストが空でないことを保証しない限り、「count-1」は悪い習慣です。

空のリストをチェックする便利な方法は、それを行うこと以外にありません。

私が考えることができる最も短い方法は

string myString = (myList.Count != 0) ? myList [ myList.Count-1 ] : "";

すべてを出て、常にtrueを返すデリゲートを作成し、それをFindLastに渡すと、最後の値(またはリストが空の場合はデフォルトで作成されたvalye)が返されます。この関数はリストの最後から始まるため、メソッドは通常O(n)ですが、Big O(1)または一定時間になります。

//somewhere in your codebase, a strange delegate is defined
private static bool alwaysTrue(string in)
{
    return true;
}

//Wherever you are working with the list
string myString = myList.FindLast(alwaysTrue);

FindDastメソッドは、デリゲート部分を数えると見苦しいですが、宣言する必要があるのは1か所だけです。リストが空の場合、文字列のリストタイプ ""のデフォルトの構成値が返されます。alwaysTrueデリゲートをさらに一歩進めて、文字列型ではなくテンプレートにすると、さらに便利です。


2
デリゲートはラムダ式で置き換えることができます。myList.FindLast(_unused_variable_name => true);これはタイプに関係なく機能します。短いバージョンはmyList.FindLast(_ => true);ですが、アンダースコア(またはその他の1文字の識別子)だけでは、少し混乱することがあります。
ボブ


5

変化する

for (int cnt3 = 0 ; cnt3 <= integerList.FindLastIndex ; cnt3++)

for (int cnt3 = 0 ; cnt3 < integerList.Count; cnt3++)

foreachの方が使いやすいことがよくありますが、少し遅くなります。
エリックJ.

カウントを使用している場合... -1を実行すると、インデックスエラーが発生します。for(int cnt3 = 0; cnt3 <integerList.Count-1; cnt3 ++)
RiddlerDev 2009

4
そのため、<=を<に変更しました。コードは投稿されたとおりです:-)
Eric J.

@Eric:以前は遅くなりましたが、JITでヒットするのは些細なケースであるため、今までにない場合は驚きます。:dunno:
Sam Harwell

1
@IPXアレス:まだあなたが反復されているデータの種類に応じて、問題になるようだ: stackoverflow.com/questions/365615/...を
エリック・J.


2

あなたは最初にリストの要素の数を数えることでそれを見つけることができます例えば。

int count = list.Count();

次に、カウントにインデックスを付けることができます-1でリストの最後の要素を取得します。

int lastNumber = list[count - 1];

2
重複した回答を投稿しないでください。
Ian Mercer


1

リストのCountプロパティを使用しないのはなぜですか?

for(int cnt3 = 0; cnt3 < integerList.Count; cnt3++)

0

元の質問とは関係なく、リストに複数回インデックスを付けるよりもローカル変数への参照をキャプチャすると、パフォーマンスが向上します。

AllIntegerIDs ids = new AllIntegerIDs();
ids.m_MessageID = (int)IntegerIDsSubstring[IntOffset];
ids.m_MessageType = (int)IntegerIDsSubstring[IntOffset + 1];
ids.m_ClassID = (int)IntegerIDsSubstring[IntOffset + 2];
ids.m_CategoryID = (int)IntegerIDsSubstring[IntOffset + 3];
ids.m_MessageText = MessageTextSubstring;
integerList.Add(ids);

そしてあなたのforループでは:

for (int cnt3 = 0 ; cnt3 < integerList.Count ; cnt3++) //<----PROBLEM HERE
{
   AllIntegerIDs ids = integerList[cnt3];
   Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}\n",
      ids.m_MessageID,ids.m_MessageType,ids.m_ClassID,ids.m_CategoryID, ids.m_MessageText);
}

-1

私はforeachの方がずっと簡単だと同意しなければなりません

foreach(AllIntegerIDs allIntegerIDs in integerList)
{
Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}\n", allIntegerIDs.m_MessageID,
allIntegerIDs.m_MessageType,
allIntegerIDs.m_ClassID,
allIntegerIDs.m_CategoryID,
allIntegerIDs.m_MessageText);
}

また、私はあなたが同じようにそれを追加することができ、あなたの.NETのバージョンに応じて、公共分野の代わりにあなたの情報にアクセスするためのプロパティを追加示唆しているpublic int MessageType {get; set;}と取り除くm_ことが可能はずのようなどのパブリックフィールド、プロパティから。


-1

これはあなたに役立つと思います。チェックしてください

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