배열 대 List <T> : 언제 사용해야합니까?


592
MyClass[] array;
List<MyClass> list;

하나가 다른 것보다 선호되는 시나리오는 무엇입니까? 그리고 왜?


9
여기서 널리 사용되는 토론 에서 볼 수 있듯이 배열은 사용되지 않습니다 . 또한 블로그에서 호스트와 호스트 지적했습니다 .
gimel

9
내가 실수하지 않으면 List <>에는 내부 구조로 배열이 있습니다. 내부 배열이 채워질 때마다 단순히 크기의 두 배 (또는 현재 크기의 다른 일정한 시간) 인 배열에 내용을 복사합니다. en.wikipedia.org/wiki/Dynamic_array
Ykok

Ykok : 당신이 말한 것, List <> 의 소스 코드를 찾았 습니다 .
Carol

19
@gimel 배열이 쓸모 없다고 주장하는 것은 아마도 약간 대담하다
awdz9nld

답변:


580

실제로 배열을 사용하려는 경우는 거의 없습니다. List<T>어레이 크기 조정은 비용이 많이 들기 때문에 언제든지 데이터를 추가 / 제거 할 때 사용 하십시오. 데이터 길이가 고정되어 있다는 것을 알고 벤치마킹 후 매우 특정한 이유로 미세 최적화하려는 경우 배열이 유용 할 수 있습니다.

List<T>LINQ는 배열보다 훨씬 더 많은 기능을 제공 하지만 거의 항상 올바른 선택입니다. params물론 논쟁을 제외하고 . ;-피

카운터로서 List<T>-1 차원입니다. 어디에 당신이 int[,]or 와 같은 직사각형 (등) 배열을 가지고 string[,,]있지만 객체 모델에서 그러한 데이터를 모델링하는 다른 방법이 있습니다 (필요한 경우).

또한보십시오:

즉, protobuf-net 프로젝트 에서 배열을 많이 사용합니다 . 전적으로 성능을 위해 :

  • 그것은 많은 비트 시프 팅을 수행하므로 byte[]인코딩에는 매우 필수적입니다.
  • byte[]기본 스트림 (및 vv)으로 보내기 전에 채우는 로컬 롤링 버퍼를 사용합니다 . BufferedStream등 보다 빠릅니다 .
  • 크기는 일단 빌드되면 고정되고 매우 빠르 므로 내부적으로 배열 기반 객체 모델 () Foo[]대신 List<Foo>을 사용합니다.

그러나 이것은 분명히 예외입니다. 일반적인 업무 처리 과정에서는 List<T>매번 승리합니다.


8
크기 조정에 대한 논쟁은 완전히 유효합니다. 그러나 사람들은 크기 조정이 필요하지 않은 경우에도 목록을 선호합니다. 후자의 경우, 견고하고 논리적 인 주장이 있습니까? 아니면 "배열이 유행을 벗어난 것"입니까?
Frederick The Fool

6
"배열 크기 조정이 비싸므로 데이터를 추가 / 제거 할 때마다 List <T>를 사용하십시오." List <T>는 내부적으로 배열을 사용합니다. LinkedList <T>를 생각하고 있었습니까?
dan-gph

14
더 많은 기능 == 더 복잡한 == 해당 기능이 필요하지 않으면 좋지 않습니다. 이 답변은 기본적으로 배열이 더 나은 이유를 나열하지만 반대 결론을 이끌어냅니다.
Eamon Nerbonne

12
당신이 그 기능을 사용하지 않는 경우 @EamonNerbonne, 나는 거의 보장 그들은 당신을 해치지 ...하지만되지 수 : 돌연변이를 필요가 없습니다 컬렉션의 수입니다 훨씬 작은, 내 경험에있는 것보다 변이
Marc Gravell

7
@MarcGravell : 코딩 스타일에 따라 다릅니다. 내 경험상 사실상 컬렉션이 변경되지 않았습니다. 그건; 콜렉션은 데이터베이스에서 검색되거나 일부 소스에서 구성되지만, 새 콜렉션 (예 : 맵 / 필터 등)을 다시 작성하여 추가 처리가 항상 수행됩니다. 개념적 돌연변이가 필요한 경우에도 새로운 컬렉션을 생성하는 것이 가장 간단한 경향이 있습니다. 필자는 컬렉션을 성능 최적화로 변경했을 뿐이며, 이러한 최적화는 로컬에 집중되어 API 소비자에게 돌연변이를 노출시키지 않는 경향이 있습니다.
Eamon Nerbonne

121

실제로 놀랍게도 링크를 추가하라는 대답은 아직 언급되지 않았습니다 . "어딘가 해로운 것으로 간주되는 배열" 에 대한 Eric의 Lippert 블로그 항목 .

실용적인 곳에서는 컬렉션을 사용하는 것이 좋다고 제목에서 판단 할 수 있지만 Marc가 올바르게 지적한 것처럼 배열이 실제로 유일한 솔루션 인 곳이 많이 있습니다.


2
마침내 3 년이 지난 후에이 책을 읽게되었습니다. 좋은 기사, 지금 좋은 기사. :)
Spencer Ruport

21

권장하는 다른 답변에도 불구하고 List<T>처리 할 때 배열을 사용하려고합니다.

  • 이미지 비트 맵 데이터
  • 다른 저수준 데이터 구조 (예 : 네트워크 프로토콜)

1
네트워크 프로토콜이 필요한 이유 오히려 여기에 사용자 정의 구조를 사용하고 특수 직렬 변환기 또는 명시 적 메모리 레이아웃을 제공하지 않습니까? 또한 List<T>바이트 배열이 아닌 here 을 사용하는 것에 대해 말하는 것은 무엇 입니까?
Konrad Rudolph

8
@Konrad-음, 우선, Stream.Read와 Stream.Write는 byte []와 함께 작동합니다.
Marc Gravell

12

실제로 성능에 관심이 없다면 "C ++ 대신 .Net을 사용하는 이유는 무엇입니까?"라는 의미입니다. List <>를 고수해야합니다. 유지 관리가 쉽고 배열 뒤에서 배열 크기를 조정하는 모든 더러운 작업을 수행합니다. (필요한 경우 List <>는 배열 크기를 선택하는 데 매우 현명하므로 일반적으로 필요하지 않습니다.)


15
"C ++ 대신 .Net을 사용하는 이유는 무엇입니까?" XNA
Bengt 2018 년

6

컬렉션 자체의 불변성이 클라이언트 및 공급자 코드 사이의 계약의 일부인 경우 (컬렉션 내의 항목의 불변성이 아닐 수 있음) IEnumerable이 적합하지 않은 경우에는 배열 을 우선적으로 사용해야 합니다.

예를 들어

var str = "This is a string";
var strChars = str.ToCharArray();  // returns array

"strChars"의 수정은 "str"의 기본 유형에 대한 구현 수준의 지식에 관계없이 원래의 "str"객체를 변경하지 않습니다.

하지만

var str = "This is a string";
var strChars = str.ToCharList();  // returns List<char>
strChars.Insert(0, 'X');

이 경우 insert 메소드가 원래 "str"오브젝트를 변경하거나 변경하지 않는 경우 해당 코드 스 니펫만으로는 명확하지 않습니다. 그러한 결정을 내리려면 String에 대한 구현 수준의 지식이 필요하며, 이는 Design by Contract 접근 방식을 위반합니다. String의 경우 큰 문제는 아니지만 거의 모든 경우에 큰 문제가 될 수 있습니다. List를 읽기 전용으로 설정하면 도움이되지만 컴파일 타임이 아닌 런타임 오류가 발생합니다.


나는 C #을 처음 접했지만 왜 목록을 반환하면 배열을 반환하지 않는 방식으로 원래 데이터의 변경 가능성을 암시하는지 명확하지 않습니다. 그래도 이름이 시작되는 메소드 To는 원래 인스턴스를 수정할 수없는 객체를 만드는 strChars as char[]것이지만, 유효한 경우 원래 객체를 수정할 수 있다고 제안하는 것과는 반대 입니다.
Tim MB

@TimMB 컬렉션의 불변성 (항목을 추가하거나 원격으로 할 수 없음)과 컬렉션의 항목의 불변성이 있습니다. 나는 후자를 언급하고있는 반면, 아마도 두 사람을 혼란스럽게 할 것입니다. 배열을 반환하면 클라이언트가 항목을 추가 / 제거 할 수 없습니다. 그렇다면 배열을 다시 할당하고 원본에 영향을 미치지 않습니다. 목록을 반환하면 그러한 보증이 없으며 원본이 영향을받을 수 있습니다 (구현에 따라 다름). 항목의 유형이 구조체가 아닌 경우 컬렉션 내에서 항목을 변경하면 (배열 또는 목록) 원본에 영향을 줄 수 있습니다.
허먼 feld 펠트

명확하게 해 주셔서 감사합니다. 여전히 혼란 스럽습니다 (아마도 C ++ 세계에서 왔기 때문에). 경우 str내부 배열을 사용하며, ToCharArray이 배열에 대한 참조를 반환 클라이언트는 변이 수 str사이즈 남아 고정해도, 해당 배열의 요소를 변화시킴으로써. 그러나 'strChars'를 수정해도 원래의 "str"객체가 변경되지는 않습니다. 내가 여기서 무엇을 놓치고 있습니까? 내가 볼 수 있듯이, 어느 경우이든 클라이언트는 내부 표현에 액세스 할 수 있으며 유형에 관계없이 어떤 종류의 돌연변이를 허용합니다.
팀 MB

3

내가 필요 해요 정확히 얼마나 많은 요소를 알고있는 경우에, 나는 5 개 요소 만 필요하다고 이제까지 나는 배열을 사용하여 5 요소. 그렇지 않으면 List <T>를 사용합니다.


1
요소 수를 아는 경우 List <T>를 사용하지 않는 이유는 무엇입니까?
Oliver

3

대부분의 경우를 사용하면 List충분합니다. A List는 내부 배열을 사용하여 데이터를 처리하고 List현재 용량보다 많은 요소를 추가 할 때 배열의 크기를 자동으로 조정 하므로 용량을 미리 알아야하는 배열보다 사용하기가 더 쉽습니다.

C #의 목록에 대한 자세한 내용 또는 디 컴파일에 대한 자세한 내용은 http://msdn.microsoft.com/en-us/library/ms379570(v=vs.80).aspx#datastructures20_1_topic5 를 참조 하십시오System.Collections.Generic.List<T> .

다차원 데이터가 필요한 경우 (예 : 매트릭스 또는 그래픽 프로그래밍) array대신에 사용할 수 있습니다.

항상 그렇듯이 메모리 나 성능에 문제가 있다면 측정하십시오! 그렇지 않으면 코드에 대해 잘못된 가정을 할 수 있습니다.


1
안녕하세요, "목록 조회 시간이 O (n)"인 이유를 설명해 주시겠습니까? 내가 아는 한 List <T>는 배후에서 배열을 사용합니다.
잠자리

1
@dragonfly 당신이 완전히 맞아요. 소스 . 당시에는 구현에 포인터가 사용되었다고 가정했지만 이후 다른 방법을 배웠습니다. 위의 링크에서 : '이 속성의 값을 검색하는 것은 O (1) 연산입니다. 속성을 설정하는 것도 O (1) 작업입니다. '
Sune Rievers

2

배열 대. 목록은 고전적인 유지 관리 성과 성능 문제입니다. 거의 모든 개발자가 따르는 규칙은 둘 다를 쏴야하지만 충돌이 발생할 경우 성능보다 유지 관리 성을 선택해야한다는 것입니다. 이 규칙의 예외는 성능이 이미 문제로 입증 된 경우입니다. 이 원리를 Arrays Vs에 적용하면. 목록을 얻으면 다음과 같이됩니다.

성능 문제가 발생할 때까지 강력한 형식의 목록을 사용하십시오. 성능 문제가 발생하면 어레이로의 드롭 아웃이 유지 관리 측면에서 솔루션에 해를 끼치는 것보다 성능이 뛰어난 솔루션에 도움이 될지 여부를 결정하십시오.


1

아직 언급되지 않은 또 다른 상황은 많은 수의 항목을 가질 때입니다. 각 항목은 서로 관련되어 있지만 독립적 인 고정 변수 묶음으로 구성됩니다 (예 : 점의 좌표 또는 3D 삼각형의 꼭짓점). 노출 된 필드 구조의 배열은 해당 요소를 다른 위치에서 불가능한 "제자리에서"효율적으로 수정할 수있게합니다. 구조의 배열은 요소를 RAM에 연속적으로 보유하므로 배열 요소에 대한 순차적 액세스는 매우 빠릅니다. 코드가 배열을 통해 많은 순차적 패스를 수행해야하는 상황에서는 구조체의 배열이 배열 또는 클래스 객체 참조의 다른 모음보다 2 : 1만큼 성능이 우수 할 수 있습니다. 더욱이,

배열의 크기를 조정할 수는 없지만 사용중인 요소 수와 함께 배열 참조를 코드에 저장하고 배열을 필요에 따라 더 큰 배열로 바꾸는 것은 어렵지 않습니다. 또는 유사하게 작동 List<T>하지만 백업 저장소를 노출 한 유형의 코드를 쉽게 작성할 수 있으므로 MyPoints.Add(nextPoint);또는 중 하나를 말할 수 있습니다 MyPoints.Items[23].X += 5;. 코드가 목록의 끝을 넘어 액세스하려고 시도하는 경우 후자가 반드시 예외를 throw하지는 않지만 사용법은 개념적으로 매우 유사합니다 List<T>.


당신이 설명한 것은 List <>입니다. 인덱서가 있으므로 기본 배열에 직접 액세스 할 수 있으며 List <>가 크기를 유지합니다.
Carl

@Carl : 예를 들어 Point[] arr;, 코드가 예를 들어 말할 수 arr[3].x+=q;있습니다. 예를 들어 List<Point> list, 대신 말할 필요가 있습니다 Point temp=list[3]; temp.x+=q; list[3]=temp;. 경우에 도움이 될 것입니다 List<T>방법을했다 Update<TP>(int index, ActionByRefRef<T,TP> proc, ref TP params). 컴파일러로 전환 할 수 list[3].x+=q;{list.Update(3, (ref int value, ref int param)=>value+=param, ref q);있지만 그러한 기능은 없습니다.
supercat

좋은 소식. 효과가있다. list[0].X += 3;목록의 첫 번째 요소의 X 속성에 3을 추가합니다. 그리고 listA는 List<Point>PointX와 Y 특성을 가진 클래스

1

.NET의 목록은 배열의 래퍼이며 내부적으로 배열을 사용합니다. 목록에 대한 작업의 시간 복잡도는 배열에서와 동일하지만 목록의 추가 된 기능 / 사용 편의성 (예 : 자동 크기 조정 및 목록 클래스와 함께 제공되는 메서드)으로 인해 약간의 오버 헤드가 있습니다. 매우 최적화 된 코드를 작성해야하거나 배열 주위에 빌드 된 다른 코드로 작업하는 경우와 같이 강력 하지 않은 이유가 없다면 모든 경우에 목록을 사용하는 것이 좋습니다 .


0

각 데이터 유형의 기능을 비교하는 것이 아니라 가장 실용적인 대답은 "특히 차이점은 구현해야하기 때문에 달성해야 할 사항에 중요하지 않을 것입니다 IEnumerable. 따라서 널리 사용되는 규칙을 따르고 List하지 말아야 할 이유가있을 때까지는 아마도 List.

대부분의 경우 관리 코드에서 마이크로 최적화에 대한 걱정보다 가능한 한 쉽게 작업 할 수있는 컬렉션을 선호합니다.


0

그것들은 인기가 없지만 게임 프로젝트에서 Arrays의 팬입니다. -반복 속도는 경우에 따라 중요 할 수 있습니다. 각 요소마다 많은 작업을 수행하지 않으면 각 어레이의 오버 헤드가 현저히 줄어 듭니다 .- 도우미 기능으로는 추가 및 제거가 그리 어렵지 않습니다. 그것은 중요하지 않을 수 있습니다-대부분의 경우 여분의 메모리가 낭비됩니다 (구조 배열에서만 중요합니다)-약간 적은 쓰레기와 포인터 및 포인터 추적

즉, 실제로는 Array보다 List를 훨씬 더 자주 사용하지만 각각의 위치가 있습니다.

래퍼 및 열거 오버 헤드를 최적화 할 수 있도록 내장 유형을 나열하면 좋을 것입니다.


0

배열보다 목록을 채우는 것이 더 쉽습니다. 배열의 경우 정확한 데이터 길이를 알아야하지만 목록의 경우 데이터 크기는 어느 것이나 될 수 있습니다. 또한 목록을 배열로 변환 할 수 있습니다.

List<URLDTO> urls = new List<URLDTO>();

urls.Add(new URLDTO() {
    key = "wiki",
    url = "https://...",
});

urls.Add(new URLDTO()
{
    key = "url",
    url = "http://...",
});

urls.Add(new URLDTO()
{
    key = "dir",
    url = "https://...",
});

// convert a list into an array: URLDTO[]
return urls.ToArray();

0

아무도 언급하지 않았으므로 C #에서 배열은 목록입니다. MyClass[]그리고 List<MyClass>둘 다 구현 IList<MyClass>합니다. (예 void Foo(IList<int> foo)처럼 호출 할 수 있습니다 Foo(new[] { 1, 2, 3 })또는 Foo(new List<int> { 1, 2, 3 }))

따라서 a List<MyClass>를 인수로 허용 하지만 기능의 하위 집합 만 사용 하는 메소드를 작성하는 경우 IList<MyClass>호출자의 편의를 위해 대신 선언 할 수 있습니다 .

세부:


"C #에서 배열은 목록입니다" 사실이 아닙니다. 배열은가 아니며 인터페이스 List만 구현합니다 IList.
Rufus L

-1

그것은 데이터 구조가 필요한 상황에 전적으로 달려 있습니다. 예를 들어 List를 사용하여 다른 기능이나 서비스에서 사용할 항목을 작성하는 경우이를 수행하는 완벽한 방법입니다.

이제 항목 목록이 있고 표시하려는 경우 웹 페이지 배열에 사용해야 할 컨테이너가 있습니다.


1
항목 목록이 있고 표시하려는 경우 이미 가지고있는 목록을 사용하면 무엇이 잘못됩니까? 배열은 여기서 무엇을 제공할까요?
Marc Gravell

1
그리고 "다른 기능이나 서비스가 사용할 아이템을 만들기"위해 실제로 반복자 블록을 선호합니다. IEnumerable<T>그러면 버퍼링하는 대신 객체를 스트리밍 할 수 있습니다.
Marc Gravell

-1

현장에서 캐스팅을 수행하는 능력에 대해 언급 할 가치가 있습니다.

interface IWork { }
class Foo : IWork { }

void Test( )
{
    List<Foo> bb = new List<Foo>( );
    // Error: CS0029 Cannot implicitly convert type 'System.Collections.Generic.List<Foo>' to 'System.Collections.Generic.List<IWork>'
    List<IWork> cc = bb; 

    Foo[] bbb = new Foo[4];
    // Fine
    IWork[] ccc = bbb;
}

따라서 Array 는 함수의 반환 유형이나 인수에 사용될 때 약간의 유연성을 제공합니다.

IWork[] GetAllWorks( )
{
    List<Foo> fooWorks = new List<Foo>( );
    return fooWorks.ToArray( ); // Fine
}

void ExecuteWorks( IWork[] works ) { } // Also accept Foo[]

배열의 분산 깨졌습니다 .
mcarton
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.