Làm cách nào tôi có thể tìm thấy phần tử cuối cùng trong Danh sách <>?


172

Sau đây là một trích xuất từ ​​mã của tôi:

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

Tôi đang cố gắng sử dụng như sau trong mã hàm main () của mình:

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

Vấn đề nằm ở đây: Tôi đang cố in tất cả các thành phần trong Danh sách của mình bằng vòng lặp 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);
}

Tôi muốn tìm phần tử cuối cùng để tôi đánh đồng cnt3 trong vòng lặp for của mình và in ra tất cả các mục trong Danh sách. Mỗi phần tử trong danh sách là một đối tượng của lớp AllIntegerID như đã đề cập ở trên trong mẫu mã. Làm cách nào để tìm mục nhập hợp lệ cuối cùng trong Danh sách?

Tôi có nên sử dụng một cái gì đó như số nguyênList.Find (số nguyên []. M_MessageText == null;

Nếu tôi sử dụng nó, nó sẽ cần một chỉ số sẽ nằm trong khoảng từ 0 đến tối đa. Có nghĩa là tôi sẽ phải sử dụng một vòng lặp khác mà tôi không có ý định sử dụng. Có cách nào ngắn hơn / tốt hơn không?

Cảm ơn, Viren


@Viren: Tôi thụt mã để làm cho nó hiển thị đúng. Nếu bạn chỉnh sửa theo tôi, bạn có thể chắc chắn rằng tôi đã không hoàn tác chúng không?
Sam Harwell

8
Không liên quan đến câu hỏi của bạn, nhưng bạn thực sự không nên thực hiện một bộ hoàn thiện trừ khi nó là cần thiết.
Brian Rasmussen

Không liên quan đến câu hỏi, nhưng để dễ đọc và dễ bảo trì, tôi khuyên bạn nên làm điều đó AllIntegerIDs newItem = new AllIntegerID();, sử dụng nó để gán tất cả các trường và sau đó gọi integerList.Add(newItem). Hoặc sử dụng các thuộc tính thay vì các trường và sử dụng cú pháp khởi tạo đối tượng C # 3.0.
Thorarin

Câu trả lời:


207

Nếu bạn chỉ muốn truy cập vào mục cuối cùng trong danh sách, bạn có thể làm

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

để có được tổng số mục trong danh sách, bạn có thể sử dụng thuộc tính Đếm

var itemCount = integerList.Count;

17
@Jared Tôi nghĩ bạn đã quên thêm dòng này "if (integList.Count! = 0)" trước dòng đầu tiên
prabhakaran

21
IMHO điều này không xứng đáng là câu trả lời hàng đầu, nó đọc khủng khiếp và để lại cơ hội cho một lỗi nếu số lượng bằng không. Cách tiếp cận CleanCode ™ sẽ là sử dụng Last/ LastOrDefaultnhư được đề cập dưới đây.
chiliitom

2
Như đã chỉ ra trước đây, câu trả lời này không tính đến tình huống khi danh sách trống và không nên sử dụng IMHO.
merrr

2
@chillitom @merrr Sử dụng các phương pháp mở rộng LINQ không giúp được gì. Enumerable.Lastsẽ ném một ngoại lệ nếu danh sách trống. Nếu bạn gọiEnumerable.LastOrDefault và chuyển một danh sách các loại giá trị, giá trị mặc định sẽ được trả về nếu danh sách trống. Vì vậy, nếu bạn nhận được 0 trở lại từ một List<int>bạn sẽ không biết danh sách trống hay giá trị cuối cùng là 0. Tóm lại, bạn cần kiểm tra Countcơ chế truy xuất nào bạn quyết định sử dụng.
0b101010

4
@chillitom Mỗi người để riêng mình. Trong trường hợp bạn biết một danh sách được điềnvar element = list[list.Count - 1] là rất ngắn gọn và dễ đọc. Không cần phải gọi các phương thức mở rộng
0b101010

276

Để có được mục cuối cùng của bộ sưu tập, hãy sử dụng các phương thức mở rộng LastOrDefault ()Last ()

var lastItem = integerList.LastOrDefault();

HOẶC LÀ

var lastItem = integerList.Last();

Remeber để thêm using System.Linq;, hoặc phương pháp này sẽ không có sẵn.


17
Đúng, đây là cách tốt nhất, Last và LastOrDefault được tối ưu hóa cho Danh sách <> s
chillitom

2
@Gusdor Tôi chưa thấy tài liệu này nhưng tôi có xu hướng chỉ chuyển sang các nguồn (hoặc sử dụng một trình phân tách như Resharper, dotPeek hoặc ILSpy) cho những điều này. Từ đó tôi có thể thấy rằng First, FirstOrDefault, Last, LastOrDefault, Single, SingleOrDefault, ElementAtElementAtOrDefaultđược tối ưu hóa cho IList<TSource>, CountContainsđược tối ưu hóa cho ICollection<TSource>Cast<TResult>được tối ưu hóa cho IEnumerable<TResult>.
chillitom

8
đảm bảo thêmusing System.Linq;
Kết hợp

4
@chillitom Các phương thức mở rộng được hiển thị bởi System.Linq.Enumerablekhông thực sự 'tối ưu hóa'. Đây là mã cho Enumerable.Lastphương thức.
0b101010

4
@chillitom sau khi đọc nguồn của System.Linq.Enumerable.Last, tôi đồng ý với 0b101010 - Last()không được "tối ưu hóa cho List<>s" - Last()chỉ là một trình bao bọc xấu xí, mặc định return list[list.Count-1]trong trường hợp đối số là một IListlặp đi lặp lại trong danh sách cho đến khi kết thúc trong trường hợp nó không ... làm cho nó trở thành một giải pháp rất kém nếu đó IListlà một LinkedList, vì người lập chỉ mục sẽ đi qua toàn bộ danh sách một cách không cần thiết (Tôi không tìm thấy ghi đè lặp đi lặp lại Item[]với chỉ số> Đếm / 2 trong các nguồn c #, YMMV )

20

Hãy tìm hiểu tận gốc câu hỏi, làm thế nào để giải quyết phần tử cuối cùng của Danh sách một cách an toàn ...

Giả định

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

Sau đó

//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" là một thói quen xấu trừ khi trước tiên bạn đảm bảo danh sách không trống.

Không có cách nào thuận tiện để kiểm tra danh sách trống ngoại trừ việc thực hiện.

Cách ngắn nhất tôi có thể nghĩ là

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

bạn có thể đi ra ngoài và tạo một đại biểu luôn trả về giá trị true và chuyển nó tới FindLast, nó sẽ trả về giá trị cuối cùng (hoặc valye được xây dựng mặc định nếu danh sách trống). Hàm này bắt đầu ở cuối danh sách, do đó sẽ là Big O (1) hoặc thời gian không đổi, mặc dù phương thức thường là 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);

Phương thức FindLast là xấu nếu bạn đếm phần đại biểu, nhưng nó chỉ cần được khai báo một nơi. Nếu danh sách trống, nó sẽ trả về giá trị được xây dựng mặc định của loại danh sách "" cho chuỗi. Việc luôn luôn ủy nhiệm luôn tiến một bước, làm cho nó trở thành một mẫu thay vì kiểu chuỗi, sẽ hữu ích hơn.


2
Đại biểu có thể được thay thế bằng biểu thức lambda: myList.FindLast(_unused_variable_name => true);Điều này sẽ hoạt động bất kể loại nào. Một phiên bản ngắn hơn là myList.FindLast(_ => true);, nhưng tôi thấy chỉ có dấu gạch dưới (hoặc bất kỳ định danh ký tự đơn nào khác) đôi khi có thể gây nhầm lẫn.
Bob


5

Thay đổi

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

đến

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

foreach thường thuận tiện hơn để sử dụng, nhưng chậm hơn.
Eric J.

nếu sử dụng Count ... hãy thực hiện -1 hoặc bạn sẽ gặp lỗi chỉ mục. for (int cnt3 = 0; cnt3 <integList.Count - 1; cnt3 ++)
RiddlerDev

4
Đó là lý do tại sao tôi thay đổi <= thành <. Mã này đúng như đã đăng :-)
Eric J.

@Eric: Nó từng chậm hơn, nhưng đó là một trường hợp tầm thường để đánh vào JIT vì vậy tôi sẽ ngạc nhiên nếu bây giờ họ không có. : dunno:
Sam Harwell

1
@IPX Ares: Có vẻ vẫn là một vấn đề, tùy thuộc vào loại dữ liệu bạn đang lặp: stackoverflow.com/questions/365615/ Kẻ
Eric J.

2

Sử dụng Counttài sản. Chỉ số cuối cùng sẽ là Count - 1.

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

2

Bạn có thể tìm thấy nó bằng cách đếm số phần tử đầu tiên trong danh sách, vd

int count = list.Count();

Sau đó, bạn có thể lập chỉ mục số đếm - 1 để lấy phần tử cuối cùng trong danh sách, vd

int lastNumber = list[count - 1];

2
Xin vui lòng không gửi câu trả lời trùng lặp.
Ian Mercer


1

Tại sao không sử dụng thuộc tính Count trong Danh sách?

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

0

Không phụ thuộc vào câu hỏi ban đầu của bạn, bạn sẽ có hiệu suất tốt hơn nếu bạn nắm bắt được các tham chiếu đến các biến cục bộ thay vì lập chỉ mục vào danh sách của bạn nhiều lần:

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

Và trong forvòng lặp của bạn :

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

Tôi sẽ phải đồng ý một bài giảng sẽ dễ dàng hơn rất nhiều

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

Ngoài ra, tôi sẽ đề nghị bạn thêm các thuộc tính để truy cập thông tin của bạn thay vì các trường công khai, tùy thuộc vào phiên bản .net của bạn, bạn có thể thêm nó vào public int MessageType {get; set;}và loại bỏ khỏi các trường m_công khai, các thuộc tính của bạn, v.v. vì nó không nên ở đó.


-1

Tôi nghĩ rằng điều này sẽ giúp bạn. Hãy kiểm tra

    TaxRangers[TaxRangers.Count]. max
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.