Як я можу знайти останній елемент у списку <>?


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

Я намагаюся використовувати наступне у своєму головному () функційному коді:

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

Я хочу знайти останній елемент, щоб я прирівнював cnt3 в моєму циклі і роздруковував усі записи в списку. Кожен елемент у списку є об'єктом класу AllIntegerID, як зазначено вище у зразку коду. Як знайти останній дійсний запис у Списку?

Чи слід використовувати щось на зразок integerList.Find (integerList []. M_MessageText == null;

Якщо я використовую, йому знадобиться індекс, який буде варіюватися від 0 до будь-якого максимуму. Значить, мені доведеться використовувати інший цикл, який я не збираюся використовувати. Чи існує коротший / кращий спосіб?

Спасибі, Вірен


@Viren: Я відрізав код, щоб він відображався належним чином. Якщо ви внесли зміни в мене, чи можете ви переконатися, що я їх не скасував?
Сем Харвелл

8
Не стосується вашого питання, але ви дійсно не повинні впроваджувати фіналізатор, якщо він не потрібен.
Брайан Расмуссен

Не пов'язано з питанням, але для читабельності та ремонтопридатності я пропоную вам це 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)" перед першим рядком
прабхакаран

21
IMHO це не заслуговує на найкращу відповідь, вона читається жахливо і залишає шанс на помилку, якщо кількість нульових. Для підходу CleanCode ™ слід використовувати Last/ LastOrDefaultяк зазначено нижче.
chillitom

2
Як було зазначено раніше, ця відповідь не враховує ситуацію, коли список порожній і не повинен використовуватися IMHO.
merrr

2
@chillitom @merrr Використання методів розширення LINQ не допомагає. Enumerable.Lastвикине виняток, якщо список порожній. Якщо ви зателефонуєте Enumerable.LastOrDefaultта передасте список типів значень, значення за замовчуванням повернеться, якщо список порожній. Тож якщо ви отримаєте 0 назад від a, List<int>ви не знатимете, чи список був порожнім чи останнє значення було 0. Коротше кажучи, вам потрібно перевірити той Countмеханізм пошуку, який ви вирішите використовувати.
0b101010

4
@chillitom Кожен по-своєму. У тих випадках, коли ви знаєте, що список заповнений, я думаю, що var element = list[list.Count - 1]це дуже короткий і читабельний. Не потрібно застосовувати методи розширення
0b101010

276

Щоб отримати останній елемент колекції, використовуйте методи розширення LastOrDefault () та Last ()

var lastItem = integerList.LastOrDefault();

АБО

var lastItem = integerList.Last();

Запомніть using System.Linq;, або цей метод буде недоступний.


17
Так, це найкращий спосіб, Last and LastOrDefault оптимізовано для Список <> s
chillitom

2
@Gusdor Я не бачив це документально, але я схильний просто звертатися до джерел (або використовувати для цих речей розбірник типу Resharper, dotPeek або ILSpy). Звідти я можу бачити , що First, FirstOrDefault, Last, LastOrDefault, Single, SingleOrDefault, ElementAtі ElementAtOrDefaultоптимізовані для роботи IList<TSource>, Countі Containsоптимізовані для роботи ICollection<TSource>і Cast<TResult>оптимізований для IEnumerable<TResult>.
chillitom

8
обов'язково додайтеusing System.Linq;
Гібрид

4
@chillitom Методи розширення, яким піддаються, System.Linq.Enumerableнасправді не «оптимізовані». Ось код Enumerable.Lastметоду.
0b101010

4
@chillitom, прочитавши джерело System.Linq.Enumerable.Last, я погоджуюся з 0b101010 - Last()код не "оптимізований для List<>s" - Last()це просто некрасива обгортка, яка за замовчуванням return list[list.Count-1]у випадку, якщо аргумент є IList, і повторюється над списком до кінця у випадку це не ... що робить це дуже поганим рішенням, якщо IListце a LinkedList, оскільки індексатор просто пройде весь список без потреби (я не знайшов переосмислення ітераційного зворотнього руху Item[]з індексом> Count / 2 у джерелах c #, YMMV )

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 (1) або постійним часом, незважаючи на метод, який зазвичай є 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);

Метод FindLast некрасивий, якщо ви рахуєте частину делегата, але його потрібно оголосити лише одним місцем. Якщо список порожній, він поверне за замовчуванням сконструйоване значення типу списку для рядка. Зробити делегат alwaysTrue ще на крок, зробити його шаблоном замість рядкового типу буде корисніше.


2
Делегата можна замінити лямбда-виразом: myList.FindLast(_unused_variable_name => true);це буде працювати незалежно від типу. Коротка версія є myList.FindLast(_ => true);, але я вважаю, що лише підкреслення (або будь-який інший ідентифікатор єдиного символу) часом може бути дещо заплутаним.
Боб


5

Зміна

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

до

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

foreach часто зручніший у використанні, але це НЕБЕЗПЕЧНО повільніше.
Ерік Дж.

якщо використовується Count ... зробіть -1 або ви отримаєте індексну помилку. for (int cnt3 = 0; cnt3 <integerList.Count - 1; cnt3 ++)
RiddlerDev

4
Тому я змінив <= на <. Код правильний, як розміщено :-)
Ерік Дж.

@Eric: Раніше це було повільніше, але це неприємний випадок, щоб потрапити в JIT, тому я би здивувався, якщо вони цього не зробили. : dunno:
Сем Харвелл

1
@IPX Ares: Здається, це все ще залишається проблемою, залежно від типу даних, який ви ітератуєте: stackoverflow.com/questions/365615/…
Eric J.


2

Ви можете знайти його, спершу підрахувавши кількість елементів у списку, наприклад

int count = list.Count();

Тоді ви можете проіндексувати кількість - 1, щоб отримати останній елемент у списку, наприклад

int lastNumber = list[count - 1];

2
Будь ласка, не публікуйте повторюваних відповідей.
Ян Мерсер


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(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_від своїх публічних полів, властивостей тощо, як це не повинно бути там.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.