List <T>를 (용량이 아닌) 주어진 크기로 초기화하는 방법은 무엇입니까?


111

.NET은 성능이 거의 동일한 일반 목록 컨테이너를 제공합니다 (배열 대 목록의 성능 질문 참조). 그러나 초기 화면에서 상당히 다릅니다.

배열은 기본값으로 초기화하기가 매우 쉽고 정의에 따라 이미 특정 크기가 있습니다.

string[] Ar = new string[10];

임의의 항목을 안전하게 할당 할 수 있습니다.

Ar[5]="hello";

목록을 사용하면 더 까다 롭습니다. 동일한 초기화를 수행하는 두 가지 방법을 볼 수 있습니다. 둘 다 우아하다고 부르는 것이 아닙니다.

List<string> L = new List<string>(10);
for (int i=0;i<10;i++) L.Add(null);

또는

string[] Ar = new string[10];
List<string> L = new List<string>(Ar);

더 깨끗한 방법은 무엇입니까?

편집 : 지금까지 답변은 목록을 미리 채우는 것보다 다른 용량을 나타냅니다. 예를 들어, 10 명의 용량으로 방금 생성 된 목록에서L[2]="somevalue"

편집 2 : 사람들은 내가 목록을 사용하려는 방식이 아니기 때문에 왜 이런 식으로 목록을 사용하고 싶은지 궁금합니다. 두 가지 이유를 알 수 있습니다.

  1. 목록이 "차세대"배열이라고 설득력있게 주장 할 수 있으므로 거의 패널티없이 유연성을 추가 할 수 있습니다. 따라서 기본적으로 사용해야합니다. 초기화하기가 쉽지 않을 수 있음을 지적하고 있습니다.

  2. 내가 현재 쓰고있는 것은 더 큰 프레임 워크의 일부로 기본 기능을 제공하는 기본 클래스입니다. 내가 제공하는 기본 기능에서 목록의 크기는 고급으로 알려져 있으므로 배열을 사용할 수 있습니다. 그러나 모든 기본 클래스에 동적으로 확장 할 수있는 기회를 제공하고 싶으므로 목록을 선택합니다.


1
"편집 : 지금까지의 답변은 목록을 미리 채우는 것 이외의 다른 용량에 대한 것입니다. 예를 들어, 용량 10으로 방금 만든 목록에서는 L [2] ="somevalue "를 수행 할 수 없습니다." 수정, 아마도 질문 제목을
바꾸어야합니다

그러나 빈 값으로 목록을 미리 채우는 용도는 무엇입니까? 그게 topicstarter 가하려는 일입니까?
Frederik Gheysels

1
위치 매핑이 그렇게 중요하다면 Dictionary <int, string>을 사용하는 것이 더 합리적이지 않습니까?
Greg D

7
List을 (를) 대체하지 않습니다 Array. 그들은 분명히 별개의 문제를 해결합니다. 고정 된 크기를 원하면 Array. 를 사용 List하면 잘못된 것입니다.
Zenexer 2013

5
나는 항상 "내가 왜 필요한지 모르겠어 ..."와 같은 논쟁에서 망치려는 답을 찾는다. 그것은 단지 그것을 의미합니다 : 당신은 그것을 볼 수 없었습니다. 반드시 다른 의미는 없습니다. 사람들이 문제에 대해 더 나은 접근 방식을 제안하고 싶어한다는 점을 존중하지만, "목록이 정말 필요합니까? 문제에 대해 더 많이 말한 경우 ..."와 같이 좀 더 겸손하게 표현해야합니다. 이 방법은 참여 쾌적하게, 그리고 자신의 문제를 개선하기 위해 OP를 권장합니다. 승자가 되십시오-겸손하십시오.
AnorZaken

답변:


79

나는 이것이 자주 필요하다고 말할 수 없습니다. 왜 이것을 원하는지에 대해 더 자세히 설명해 주시겠습니까? 아마도 도우미 클래스의 정적 메서드로 넣을 것입니다.

public static class Lists
{
    public static List<T> RepeatedDefault<T>(int count)
    {
        return Repeated(default(T), count);
    }

    public static List<T> Repeated<T>(T value, int count)
    {
        List<T> ret = new List<T>(count);
        ret.AddRange(Enumerable.Repeat(value, count));
        return ret;
    }
}

당신은 할 수 사용 Enumerable.Repeat(default(T), count).ToList()하지만 크기 조정 버퍼하는 비효율적으로 인한 것입니다.

경우 참고 T참조 형식은, 그것은 저장합니다 count에 대한 통과 기준의 사본 value그들은 모두 같은 객체를 참조 할 수 있도록 - 매개 변수를. 사용 사례에 따라 원하는 것일 수도 있고 아닐 수도 있습니다.

편집 : 주석에서 언급했듯이 Repeated원하는 경우 루프를 사용하여 목록을 채울 수 있습니다. 그것도 약간 더 빠를 것입니다. 개인적으로 Repeat더 설명적인 코드를 사용 하고 있으며 실제 환경에서는 성능 차이가 무관하다고 생각하지만 마일리지는 다를 수 있습니다.


1
나는 이것이 오래된 게시물이라는 것을 알고 있지만 궁금합니다. Enumerable.Repeat는 링크의 마지막 부분 ( dotnetperls.com/initialize-array )에 따라 for 루프에 비해 훨씬 더 나쁩니다 . 또한 AddRange ()에는 msdn에 따라 O (n) 복잡성이 있습니다. 간단한 루프 대신 주어진 솔루션을 사용하는 것이 약간 비생산적이지 않습니까?
Jimmy

@Jimmy : 두 가지 접근 방식 모두 O (n)이 될 것이며,이 접근 방식은 제가 달성하고자하는 것을 더 잘 설명하고 있습니다. 루프를 선호한다면 자유롭게 사용하십시오.
Jon Skeet 2014 년

@Jimmy : 또한 벤치 마크에서를 사용 Enumerable.Repeat(...).ToArray()하고 있는데, 이것이 내가 사용하는 방식 이 아닙니다 .
Jon Skeet 2014 년

1
나는 Enumerable.Repeat()다음과 같은 방식으로 사용했습니다 ( PairC ++와 동일하게 구현 됨 ) : Enumerable.Repeat( new Pair<int,int>(int.MaxValue,-1), costs.Count)부작용을 알아 차리고 List단일 객체에 대한 참조 사본으로 가득 차 있습니다. 같은 요소를 myList[i].First = 1변경하면 전체의 모든 단일 요소가 변경되었습니다 List. 이 버그를 찾는 데 몇 시간이 걸렸습니다. 여러분은이 문제에 대한 해결책을 알고 .Add(new Pair...)있습니까? ( 단지 공통 루프를 사용하고 사용하는 것을 제외하고는 ?
00zetti

1
@ Pac0, 방금 아래에 답변을 추가했습니다. 수정 사항이 승인되지 않을 수 있습니다.
Jeremy Ray Brown

136
List<string> L = new List<string> ( new string[10] );

3
그것은 잠재적으로 서투른 같은 질문 본문에 언급되었지만 - - 개인적으로 나는 이것이 가장 깨끗한 방법입니다 생각하는 이유를 모른다
hello_earth

4
+1 : 인덱스를 통해 채우기 전에 고정 된 null 집합으로 초기화 할 가변 크기 목록이 필요한 상황에 처했습니다 (그리고 나중에 추가 항목을 추가하므로 배열이 적합하지 않음). 이 대답은 실용적이고 단순하다는 것에 대한 나의 투표를 얻습니다.
Gone Coding

멋진 대답입니다. @GoneCoding 내부적으로 새로 초기화 된 목록 L이 단순히 메모리를 string[10]그대로 재사용 하는지 (목록의 백업 저장소도 배열 임) 아니면 자체 새 메모리를 할당 한 다음 내용을 복사 string[10]하는지 궁금합니다. 이후 경로를 선택하면 string[10]자동으로 가비지 수집됩니다 L.
RBT

3
@RBT 이것이이 접근 방식의 유일한 단점입니다. 배열이 재사용 되지 않으므로 일시적으로 배열의 두 복사본을 갖게됩니다 (List는 알고있는 것처럼 내부적으로 배열을 사용합니다). 드물게 많은 수의 요소 나 메모리 제약 이있는 경우 문제가 될 수 있습니다 . 그리고 예, 추가 배열은 List 생성자가 완료 되 자마자 가비지 수집에 적합합니다 (그렇지 않으면 끔찍한 메모리 누수가 발생했을 것입니다). 적격은 "즉시 수집"을 의미하는 것이 아니라 다음에 가비지 수집이 실행될 때를 의미합니다.
AnorZaken

2
이 답변은 10 개의 새 문자열을 할당 한 다음 필요에 따라 복사하여 반복합니다. 큰 배열로 작업하는 경우; 그런 다음 받아 들인 대답보다 두 배의 메모리가 필요하므로 이것을 고려하지 마십시오.
UKMonkey

22

int ( "capacity")를 인수로 사용하는 생성자를 사용하십시오.

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

편집 : 나는 Frederik에 동의한다는 것을 추가해야합니다. 당신은 처음에 그것을 사용하는 전체적인 추론에 반하는 방식으로 목록을 사용하고 있습니다.

EDIT2 :

편집 2 : 현재 작성중인 것은 더 큰 프레임 워크의 일부로 기본 기능을 제공하는 기본 클래스입니다. 내가 제공하는 기본 기능에서 목록의 크기는 고급으로 알려져 있으므로 배열을 사용할 수 있습니다. 그러나 모든 기본 클래스에 동적으로 확장 할 수있는 기회를 제공하고 싶으므로 목록을 선택합니다.

모든 null 값이있는 List의 크기를 알아야하는 이유는 무엇입니까? 목록에 실제 값이 없으면 길이가 0이 될 것으로 예상합니다. 어쨌든 이것이 어색하다는 사실은 그것이 클래스의 의도 된 용도에 위배된다는 것을 보여줍니다.


46
이 답변은 목록에 10 개의 null 항목을 할당하지 않습니다 (요구 사항 임) . 목록의 크기 조정 (즉, 용량) 이 필요하기 전에 10 개 항목에 대한 공간 만 할당 하므로 new List<string>()문제가 발생하는 한 다르지 않습니다. . 그래도 많은 up-votes를 얻기 위해 잘했습니다 :)
Gone Coding

2
오버로드 된 생성자는 "크기"나 "길이"가 아닌 "초기 용량"값이며 항목도 초기화하지 않습니다.
Matt Wilko 2014

"누군가가 이것을 필요로 하는가"에 대한 대답 : 딥 클로닝 트리 데이터 구조를 위해 지금 당장 필요합니다. 노드는 자식을 채우거나 채우지 않을 수 있지만 기본 노드 클래스는 모든 자식 노드로 자신을 복제 할 수 있어야합니다. Blah, blah 훨씬 더 복잡합니다. 하지만 여전히 비어있는 목록을 채우고 list[index] = obj;다른 목록 기능을 사용 하려면 둘 다 필요합니다 .
Bitterblue

3
@Bitterblue : 또한 모든 값이 앞에 있지 않을 수있는 사전 할당 된 매핑입니다. 확실히 용도가 있습니다. 나는 경험이 적은 날 에이 답변을 썼습니다.
Ed S.

8

먼저 원하는 항목 수로 배열을 만든 다음 배열을 List로 변환합니다.

int[] fakeArray = new int[10];

List<int> list = fakeArray.ToList();

7

고정 값으로 초기화하려면 왜 List를 사용합니까? 성능을 위해 초기 용량을 제공하고 싶지만 필요할 때 확장 할 수있는 일반 어레이에 비해 목록의 장점 중 하나가 아님을 이해할 수 있습니다.

이렇게하면 :

List<int> = new List<int>(100);

용량이 100 개의 정수인 목록을 만듭니다. 즉, 101 번째 항목을 추가 할 때까지 목록이 '성장'할 필요가 없습니다. 목록의 기본 배열은 길이 100으로 초기화됩니다.


1
"고정 된 값으로 초기화하려면 왜 List를 사용 하는가?"좋은 점입니다.
Ed S.

7
그는 각 요소가 초기화되고 목록에 용량이 아닌 크기가있는 목록을 요청했습니다. 이 대답은 현재로서는 틀 렸습니다.
Nic Foster

질문에 대한 비판이 아니라 대답이 필요합니다. 때때로 배열을 사용하는 것과는 반대로 이런 방식으로 수행해야하는 이유가 있습니다. 이것이 왜 그런지에 관심이 있다면 Stack Overflow 질문을 할 수 있습니다.
Dino Dini

5

이와 같이 목록의 내용을 초기화하는 것은 목록의 용도가 아닙니다. 목록은 개체를 보관하도록 설계되었습니다. 특정 숫자를 특정 개체에 매핑하려면 목록 대신 해시 테이블이나 사전과 같은 키-값 쌍 구조를 사용하는 것이 좋습니다.


이것은 질문에 대한 대답이 아니라 단지 질문하지 않는 이유를 제공합니다.
Dino Dini

5

데이터와 위치 연관이 필요하다는 점을 강조하는 것 같습니다. 연관 배열이 더 적합하지 않습니까?

Dictionary<int, string> foo = new Dictionary<int, string>();
foo[2] = "string";

이것은 묻는 질문과 다른 질문에 대한 대답입니다.
Dino Dini

4

일부 고정 값의 N 요소로 목록을 초기화하려면 다음을 수행하십시오.

public List<T> InitList<T>(int count, T initValue)
{
  return Enumerable.Repeat(initValue, count).ToList();
}

이 기술에 의한 버퍼 크기 조정에 대한 우려는 John Skeet의 답변을 참조하십시오.
Amy B

1

Linq를 사용하여 기본값으로 목록을 영리하게 초기화 할 수 있습니다. ( David B의 대답과 유사합니다 .)

var defaultStrings = (new int[10]).Select(x => "my value").ToList();

한 단계 더 나아가 각 문자열을 "문자열 1", "문자열 2", "문자열 3"등의 고유 한 값으로 초기화합니다.

int x = 1;
var numberedStrings = (new int[10]).Select(x => "string " + x++).ToList();

1
string [] temp = new string[] {"1","2","3"};
List<string> temp2 = temp.ToList();

List <string> temp2 = new List <string> (temp); 이미 제안한 OP처럼.
Ed S.

Ed – 이것은 실제로 질문에 답합니다 – 그것은 능력에 관한 것이 아닙니다.
Boaz

그러나 OP는 이미이 솔루션을 좋아하지 않는다고 말했습니다.
Ed S.

1

수락 된 답변 (녹색 확인 표시가있는 답변)에 문제가 있습니다.

문제 :

var result = Lists.Repeated(new MyType(), sizeOfList);
// each item in the list references the same MyType() object
// if you edit item 1 in the list, you are also editing item 2 in the list

위의 줄을 변경하여 개체를 복사하는 것이 좋습니다. 그것에 대한 많은 다른 기사가 있습니다.

NULL이 아닌 기본 생성자를 사용하여 목록의 모든 항목을 초기화하려면 다음 메서드를 추가합니다.

public static List<T> RepeatedDefaultInstance<T>(int count)
    {
        List<T> ret = new List<T>(count);
        for (var i = 0; i < count; i++)
        {
            ret.Add((T)Activator.CreateInstance(typeof(T)));
        }
        return ret;
    }

1
이것은 "문제"가 아니라 참조를 복사하는 정상적인 동작입니다. 상황을 모르면 개체를 복사하는 것이 적합한 지 알 수 없습니다. 문제는 목록을 복사본으로 채워야하는지 여부가 아니라 용량이있는 목록을 초기화하는 것입니다. 나는 그것이 행동의 관점에서 내 대답을 명확히 것입니다 않습니다 ,하지만 난 정말이 질문의 맥락에서 문제가 계산 생각하지 않습니다.
Jon Skeet

@JonSkeet, 기본 유형에는 괜찮지 만 참조 유형에는 문제가없는 정적 도우미에 응답했습니다. 동일한 참조 항목으로 목록이 초기화되는 시나리오는 옳지 않은 것 같습니다. 이 시나리오에서 모든 항목이 힙의 동일한 개체를 가리키는 경우에도 목록이있는 이유는 무엇입니까?
Jeremy Ray Brown

샘플 시나리오 : 처음에는 모든 요소에 대해 "알 수 없음"항목으로 문자열 목록을 채운 다음 특정 요소에 대한 목록을 수정합니다. 이 경우 모든 "Unknown"값이 동일한 문자열에 대한 참조가되는 것이 전적으로 합리적입니다. 매번 문자열을 복제하는 것은 의미가 없습니다. 동일한 객체에 대한 여러 참조로 문자열을 채우는 것은 일반적인 의미에서 "올바른"또는 "잘못된"것은 아닙니다. 독자가 행동이 무엇인지 아는 한, 특정 사용 사례를 충족하는지 여부를 결정하는 것은 독자에게 달려 있습니다 .
Jon Skeet 19

0

IList에 대한 알림 : MSDN IList 설명 : "IList 구현은 읽기 전용, 고정 크기 및 가변 크기의 세 가지 범주로 나뉩니다. (...).이 인터페이스의 일반 버전은을 참조하십시오 System.Collections.Generic.IList<T>."

IList<T>에서 상속하지는 IList않지만 및 List<T>둘 다 구현 하지만 항상 variable-size 입니다. .NET 4.5부터 AFAIK 도 있지만 원하는 고정 크기 일반 목록이 없습니다.IList<T>IListIReadOnlyList<T>


0

이것은 단위 테스트에 사용한 샘플입니다. 클래스 개체 목록을 만들었습니다. 그런 다음 forloop를 사용하여 서비스에서 기대하는 개체의 'X'개를 추가했습니다. 이렇게하면 주어진 크기에 대해 목록을 추가 / 초기화 할 수 있습니다.

public void TestMethod1()
    {
        var expected = new List<DotaViewer.Interface.DotaHero>();
        for (int i = 0; i < 22; i++)//You add empty initialization here
        {
            var temp = new DotaViewer.Interface.DotaHero();
            expected.Add(temp);
        }
        var nw = new DotaHeroCsvService();
        var items = nw.GetHero();

        CollectionAssert.AreEqual(expected,items);


    }

내가 당신들에게 도움이 되었기를 바랍니다.


-1

조금 늦었지만 제안한 첫 번째 솔루션은 나에게 훨씬 더 깔끔해 보입니다. 메모리를 두 번 할당하지 않습니다. List 생성자조차도 복사하기 위해 배열을 반복해야합니다. 내부에 null 요소 만 있는지 미리 알지 못합니다.

1.-할당 N-루프 N 비용 : 1 * allocate (N) + N * loop_iteration

2.-할당 N-할당 N + 루프 () 비용 : 2 * 할당 (N) + N * 루프 _ 반복

그러나 List가 기본 제공 클래스이기 때문에 List의 할당 루프가 더 빠를 수 있지만 C #은 jit로 컴파일됩니다.

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