Bagaimana saya dapat menemukan elemen terakhir dalam Daftar <>?


173

Berikut ini adalah ekstrak dari kode saya:

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

Saya mencoba menggunakan yang berikut ini dalam kode fungsi utama () saya:

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

Masalahnya ada di sini: Saya mencoba untuk mencetak semua elemen dalam Daftar saya menggunakan for 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);
}

Saya ingin menemukan elemen terakhir sehingga saya menyamakan cnt3 di loop untuk saya dan mencetak semua entri dalam Daftar. Setiap elemen dalam daftar adalah objek dari kelas AllIntegerIDs sebagaimana disebutkan di atas dalam contoh kode. Bagaimana cara menemukan entri terakhir yang valid dalam Daftar?

Haruskah saya menggunakan sesuatu seperti integerList.Find (integerList []. M_MessageText == null;

Jika saya menggunakan itu akan memerlukan indeks yang akan berkisar dari 0 hingga apa pun maksimum. Berarti saya harus menggunakan yang lain untuk loop yang tidak ingin saya gunakan. Apakah ada cara yang lebih pendek / lebih baik?

Terima kasih, Viren


@Viren: Saya membuat indentasi kode untuk membuatnya tampil dengan benar. Jika Anda mengedit di bawah saya, dapatkah Anda memastikan saya tidak membatalkannya?
Sam Harwell

8
Tidak terkait dengan pertanyaan Anda, tetapi Anda benar-benar tidak boleh menerapkan finalizer kecuali jika diperlukan.
Brian Rasmussen

Tidak terkait dengan pertanyaan, tetapi untuk keterbacaan dan pemeliharaan, saya sarankan Anda lakukan AllIntegerIDs newItem = new AllIntegerID();, gunakan itu untuk menetapkan semua bidang lalu panggil integerList.Add(newItem). Atau gunakan properti daripada bidang dan gunakan sintaks penginisialisasi objek C # 3.0.
Thorarin

Jawaban:


208

Jika Anda hanya ingin mengakses item terakhir dalam daftar yang dapat Anda lakukan

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

untuk mendapatkan jumlah total item dalam daftar, Anda dapat menggunakan properti Count

var itemCount = integerList.Count;

17
@ Jared Saya pikir Anda lupa menambahkan baris ini "jika (integerList.Count! = 0)" sebelum baris pertama
prabhakaran

21
IMHO ini tidak layak menjadi jawaban teratas, ia sangat buruk dan meninggalkan peluang untuk kesalahan jika jumlah nol. Pendekatan CleanCode ™ akan menggunakan Last/ LastOrDefaultseperti yang disebutkan di bawah ini.
chillitom

2
Seperti yang ditunjukkan sebelumnya, jawaban ini tidak memperhitungkan situasi akun ketika daftar kosong dan tidak boleh digunakan IMHO.
merrr

2
@chillitom @merrr Menggunakan metode ekstensi LINQ tidak membantu. Enumerable.Lastakan mengeluarkan pengecualian jika daftar kosong. Jika Anda memanggil Enumerable.LastOrDefaultdan melewati daftar tipe nilai, nilai default akan dikembalikan jika daftar kosong. Jadi, jika Anda mendapatkan kembali 0 dari List<int>Anda tidak akan tahu apakah daftar itu kosong atau nilai terakhir adalah 0. Singkatnya, Anda perlu memeriksa Countmekanisme pengambilan mana yang Anda pilih untuk digunakan.
0b101010

4
@chillitom Masing-masing untuk mereka sendiri. Dalam kasus di mana Anda tahu daftar dihuni saya pikir var element = list[list.Count - 1]sangat singkat dan mudah dibaca. Tidak perlu menggunakan metode ekstensi
0b101010

277

Untuk mendapatkan item terakhir dari koleksi, gunakan metode ekstensi LastOrDefault () dan Last ()

var lastItem = integerList.LastOrDefault();

ATAU

var lastItem = integerList.Last();

Ingat untuk menambahkan using System.Linq;, atau metode ini tidak akan tersedia.


18
Yap ini adalah cara terbaik, Last dan LastOrDefault dioptimalkan untuk Daftar <> s
chillitom

2
@Gusdor Saya belum melihatnya didokumentasikan tetapi saya cenderung hanya beralih ke sumber (atau menggunakan disassembler seperti Resharper, dotPeek atau ILSpy) langsung untuk hal-hal ini. Dari sana aku bisa melihat bahwa First, FirstOrDefault, Last, LastOrDefault, Single, SingleOrDefault, ElementAtdan ElementAtOrDefaultdioptimalkan untuk IList<TSource>, Countdan Containsdioptimalkan untuk ICollection<TSource>dan Cast<TResult>dioptimalkan untuk IEnumerable<TResult>.
chillitom

8
pastikan untuk menambahkanusing System.Linq;
Hybrid

4
@chillitom Metode ekstensi yang diekspos oleh System.Linq.Enumerabletidak benar-benar 'dioptimalkan'. Inilah kode untuk Enumerable.Lastmetode ini.
0b101010

4
@chillitom setelah membaca sumbernya System.Linq.Enumerable.Last, saya setuju dengan 0b101010 - Last()kode ini tidak "dioptimalkan untuk List<>s" - Last()hanya pembungkus yang jelek, yang secara default adalah return list[list.Count-1]seandainya argumennya adalah IList, dan beralih ke daftar sampai akhir dalam kasus itu bukan ... menjadikannya solusi yang sangat buruk jika ILista LinkedList, karena pengindeks hanya akan pergi melalui seluruh daftar sia-sia (saya belum menemukan override iterasi mundur Item[]dengan indeks> Hitung / 2 di c # sources, YMMV )

20

Mari kita dapatkan akar dari pertanyaan, bagaimana cara mengatasi elemen terakhir Daftar dengan aman ...

Asumsi

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

Kemudian

//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" adalah kebiasaan buruk kecuali Anda menjamin bahwa daftar tersebut tidak kosong.

Tidak ada cara mudah untuk memeriksa daftar kosong kecuali melakukannya.

Cara terpendek yang bisa saya pikirkan adalah

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

Anda bisa keluar semua dan membuat delegasi yang selalu mengembalikan true, dan meneruskannya ke FindLast, yang akan mengembalikan nilai terakhir (atau default dibangun valye jika daftar kosong). Fungsi ini dimulai pada akhir daftar sehingga akan menjadi Big O (1) atau waktu konstan, meskipun metode biasanya O (n).

//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);

Metode FindLast jelek jika Anda menghitung bagian delegasi, tetapi hanya perlu dinyatakan satu tempat. Jika daftar kosong, itu akan mengembalikan nilai dibangun standar dari tipe daftar "" untuk string. Mengambil delegasi alwaysTrue selangkah lebih jauh, menjadikannya templat daripada tipe string, akan lebih bermanfaat.


2
Delegasi dapat diganti dengan ekspresi lambda: myList.FindLast(_unused_variable_name => true);Ini akan berfungsi terlepas dari jenisnya. Versi yang lebih pendek myList.FindLast(_ => true);, tetapi saya menemukan garis bawah (atau pengenal karakter tunggal lainnya) dapat sedikit membingungkan di kali.
Bob


5

Perubahan

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

untuk

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

foreach sering lebih mudah digunakan, tetapi sedikit lebih lambat.
Eric J.

jika menggunakan Count ... lakukan -1 atau Anda akan mendapatkan kesalahan indeks. for (int cnt3 = 0; cnt3 <integerList.Count - 1; cnt3 ++)
RiddlerDev

4
Itu sebabnya saya berubah <= menjadi <. Kode sudah benar seperti yang diposting :-)
Eric J.

@ Eric: Dulu lebih lambat, tapi ini kasus sepele untuk memukul JIT jadi saya akan terkejut jika mereka belum melakukannya sekarang. : tak tahu:
Sam Harwell

1
@IPX Ares: Tampaknya masih menjadi masalah, tergantung pada tipe data yang Anda iterasi: stackoverflow.com/questions/365615/…
Eric J.

2

Gunakan Countproperti. Indeks terakhir adalah Count - 1.

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

2

Anda dapat menemukannya dengan terlebih dahulu menghitung jumlah elemen dalam daftar misalnya

int count = list.Count();

Kemudian Anda dapat mengindeks hitungan - 1 untuk mendapatkan elemen terakhir dalam daftar misalnya

int lastNumber = list[count - 1];

2
Tolong jangan posting jawaban duplikat.
Ian Mercer

2

Di C # 8.0 Anda bisa mendapatkan item terakhir dengan ^ penjelasan lengkap operator

List<char> list = ...;
var value = list[^1]; 

// Gets translated to 
var value = list[list.Count - 1];

1

Mengapa tidak menggunakan properti Count di Daftar?

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

0

Terlepas dari pertanyaan awal Anda, Anda akan mendapatkan kinerja yang lebih baik jika Anda menangkap referensi ke variabel lokal daripada mengindeks ke daftar Anda beberapa kali:

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

Dan di forloop Anda :

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

Saya harus setuju bahwa foreach akan jauh lebih mudah

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

Saya juga menyarankan Anda menambahkan properti untuk mengakses informasi Anda alih-alih bidang publik, tergantung pada versi .net Anda, Anda dapat menambahkannya public int MessageType {get; set;}dan menyingkirkannya dari m_bidang publik Anda, properti dll karena tidak seharusnya ada di sana.


-1

Saya pikir ini membantu Anda. Silakan periksa

    TaxRangers[TaxRangers.Count]. max
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.