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 루프를 사용하여 List의 모든 요소를 ​​인쇄하려고합니다.

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을 동일하게하고 목록의 모든 항목을 인쇄하려고합니다. 목록의 각 요소는 코드 샘플에서 위에서 언급 한대로 AllIntegerID 클래스의 객체입니다. 목록에서 마지막으로 유효한 항목을 어떻게 찾습니까?

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

그것을 사용하면 0에서 최대 범위의 인덱스가 필요합니다. 내가 사용하지 않을 다른 for 루프를 사용해야한다는 것을 의미합니다. 더 짧거나 더 나은 방법이 있습니까?

고마워, Viren


@Viren : 코드가 제대로 표시되도록 들여 쓰기했습니다. 당신이 내 아래 편집을했다면 내가 취소하지 않았는지 확인할 수 있습니까?
Sam Harwell

8
귀하의 질문과 관련이 없지만 필요하지 않은 경우 최종자를 구현해서는 안됩니다.
Brian Rasmussen

질문과 관련,하지만 가독성과 유지 보수성, 나는 당신이 제안하지 AllIntegerIDs newItem = new AllIntegerID();, 모든 필드를 할당하는 것을 사용 전화를 integerList.Add(newItem). 또는 필드 대신 속성을 사용하고 C # 3.0 개체 이니셜 라이저 구문을 사용하십시오.
Thorarin

답변:


208

목록의 마지막 항목에 액세스하려면 할 수 있습니다

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

목록의 총 항목 수를 얻으려면 Count 속성을 사용할 수 있습니다

var itemCount = integerList.Count;

17
@Jared 첫 줄 앞에 "if (integerList.Count! = 0)"줄을 추가하는 것을 잊었다 고 생각합니다
prabhakaran

21
IMHO 이것은 최고의 답변이 될 가치가 없으며, 끔찍하게 읽으며 카운트가 0이면 오류가 발생할 가능성이 있습니다. CleanCode ™ 접근 방식은 사용하는 것입니다 Last/ LastOrDefault아래에 언급 한 바와 같이.
chillitom

2
앞에서 지적했듯이이 답변은 목록이 비어 있고 IMHO를 사용해서는 안되는 상황을 고려하지 않습니다.
merrr

2
@chillitom @merrr LINQ 확장 방법을 사용하면 도움이되지 않습니다. Enumerable.Last목록이 비어 있으면 예외가 발생합니다. Enumerable.LastOrDefault값 유형 목록 을 호출 하여 전달하면 목록이 비어 있으면 기본값이 반환됩니다. 따라서 0을 다시 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
네이, 마지막으로하고 LastOrDefault리스트를위한 최선의 방법 최적화되어 <> S
chillitom

2
@Gusdor 나는 그것이 문서화 된 것을 보지 못했지만 이러한 것들을 직접 소스로 돌리거나 Resharper, dotPeek 또는 ILSpy와 같은 디스어셈블러를 사용하는 경향이 있습니다. 내가 그이 볼 수에서 First, FirstOrDefault, Last, LastOrDefault, Single, SingleOrDefault, ElementAtElementAtOrDefault에 최적화 된 IList<TSource>, Count그리고 Contains에 최적화 ICollection<TSource>Cast<TResult>최적화되어 있습니다 IEnumerable<TResult>.
chillitom

8
using System.Linq;
Hybrid

4
@chillitom 노출 방법 System.Linq.Enumerable이 실제로 '최적화' 된 것은 아닙니다. Enumerable.Last메소드 코드는 다음과 같습니다 .
0b101010

4
@chillitom의 소스를 읽은 후 System.Linq.Enumerable.Last, 나는 0b101010에 동의 - Last()코드가 되지 않는다 "에 최적화 List<>의"- Last()기본적으로 그냥 못생긴 래퍼이며, return list[list.Count-1]인수가 인 경우에 IList, 그리고 반복 경우 끝까지 목록을 통해 (가) 경우는 매우 가난한 솔루션을 만들기 ... 아니다 IList입니다 LinkedList인덱서가 그냥 거꾸로 반복 재정의 발견되지 않은 (불필요하게 전체 목록을 이동하기 때문에, Item[]인덱스> 카운트 / 2 C #을 소스에, YMMV와를 )

20

질문의 근원, List의 마지막 요소를 안전하게 처리하는 방법에 대해 알아 보겠습니다.

가정

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

FindLast 메서드는 대리자 부분을 세면 추악하지만 한 곳으로 만 선언하면됩니다. 목록이 비어 있으면 문자열에 대해 목록 유형 ""의 기본 구성 값을 반환합니다. alwaysTrue 대리자를 한 단계 더 발전시켜 문자열 형식 대신 템플릿으로 만드는 것이 더 유용합니다.


2
대리자를 람다 식으로 바꿀 수 있습니다 myList.FindLast(_unused_variable_name => true);. 유형에 관계없이 작동합니다. 짧은 버전은입니다 myList.FindLast(_ => true);.하지만 밑줄 (또는 다른 단일 문자 식별자)은 때때로 약간 혼란 스러울 수 있습니다.
Bob


5

변화

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

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

foreach는 종종 사용하기가 더 편리하지만 약간 느립니다.
Eric J.

Count ...를 사용하는 경우 -1을 수행하면 색인 오류가 발생합니다. for (int cnt3 = 0; cnt3 <integerList.Count-1; cnt3 ++)
RiddlerDev

4
그렇기 때문에 <=를 <로 변경했습니다. 코드는 게시 된대로 정확합니다 :-)
Eric J.

@ 에릭 : 그것은 느려졌지만 JIT를 치는 것은 사소한 경우이므로 지금까지하지 않았다면 놀랐습니다. : dunno :
Sam Harwell

1
@IPX Ares : 반복하는 데이터 유형에 따라 여전히 문제가있는 것 같습니다 : stackoverflow.com/questions/365615/…
Eric J.

2

Count속성을 사용하십시오 . 마지막 색인은입니다 Count - 1.

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

2

목록에서 먼저 요소 수를 세면 찾을 수 있습니다.

int count = list.Count();

그런 다음 count-1을 색인하여 목록의 마지막 요소를 얻습니다.

int lastNumber = list[count - 1];

2
중복 답변을 게시하지 마십시오.
Ian Mercer

2

C # 8.0에서는 ^ 연산자 전체 설명으로 마지막 항목을 얻을 수 있습니다.

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

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

1

왜 List에서 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.