// 3칸짜리 빈 배열 생성 (0 / null)
int[] arrayA = new int[3];
// 3칸짜리 배열 생성 후 데이터 삽입
int[] arrayB = new int[3] { 10, 20, 30 };
// 배열 크기 생략
int[] arrayC = new int[] { 10, 20, 30 };
// new 생략
// 신택스 슈거(Syntactic Sugar) : 컴파일러 자동 처리
int[] arrayD = { 10, 20, 30};
ex) int[3] { 10, 20, 30 }
| 메모리 주소 | 크기 | 변수 이름 / 역할 | 저장된 값 |
|---|---|---|---|
| 0x1000 | 8 | Object Header | runtime info |
| 0x1008 | 8 | MethodTable Pointer | type info |
| 0x1010 | 4 | Length | 3 |
| 0x1014 | 4 | Padding | (빈 값) |
| 0x1018 | 4 | array[0] | 10 |
| 0x101C | 4 | array[1] | 20 |
| 0x1020 | 4 | array[2] | 30 |
CLR은 객체 할당 시 메모리 접근 효율을 위해 일반적으로 8 바이트 정렬을 유지합니다. 따라서 배열의 데이터 시작 주소와 객체 전체 크기가 8의 배수가 되도록 패딩(Padding)이 삽입될 수 있습니다.
CPU CacheCache Line 단위로 주변 데이터까지 가져온다.Cache Hit 덕분int[,] gridA = new int[2, 3];
int[,] gridB = new int[2, 3] { { 1, 2, 3}, { 4, 5, 6} };
int[,] gridC = { { 1, 2, 3}, { 4, 5, 6} };
[1] [2] [3] [4] [5] [6]int[][] jagged = new int[3][];
jagged[0] = new int[4];
jagged[1] = new int[2];
// 빈 리스트 생성 (Capacity = 0)
List<int> listA = new List<int>();
// 초기 용량 예약
List<int> listB = new List<int>(3);
// 컬렉션 초기자
List<int> listC = new List<int> { 1, 2, 3, 4, 5 };
System.Collections.GenericT[] _items : 내부 배열int _size : 데이터 갯수int _version : 수정 횟수Count : 실제 들어있는 요소의 갯수Capacity : 리스트가 내부적으로 확보하고 있는 배열의 용량RemoveAt 을 사용해 삭제하면, 빈 공간을 메우기 위해 뒤에 있는 모든 요소의 메모리 주소를 한 칸씩 앞으로 당겨야(Shift)합니다. 데이터가 많을수록 성능 저하Add (resize) : O(N)Add : O(1)Remove, RemoveAt, Insert, Contains : O(N)ex) List(3) { 10, 20, 30 }
| 메모리 주소 | 크기 | 변수 이름 / 역할 | 저장된 값 | |
|---|---|---|---|---|
| 0x1000 | 8 Byte | Object Header | (시스템 정보) | |
| 0x1008 | 8 | MethodTable Pointer | (타입 정보 주소) | |
| 0x1010 | 8 | _items | 0x2000 | 내부 배열 주소 |
| 0x1018 | 4 | _size | 3 | 현재 데이터 갯수 |
| 0x101C | 4 | _version | 3 | 변경 횟수 |
| 메모리 주소 | 크기 | 변수 이름 / 역할 | 저장된 값 |
|---|---|---|---|
| 0x2000 | 8 | Object Header | runtime info |
| 0x2008 | 8 | MethodTable Pointer | type info |
| 0x2010 | 4 | Length | 4 |
| 0x2014 | 4 | Padding | (빈 값) |
| 0x2018 | 4 | _items[0] | 10 |
| 0x201C | 4 | _items[1] | 20 |
| 0x2020 | 4 | _items[2] | 30 |
| 0x2024 | 4 | _items[3] | 0 |
| 0x2028 | 4 | padding | (빈 값) |
리스트의 내부는 결국 배열이기 때문에, 아주 미세한 속도 차이 발생
배열이나 컬렉션을 한 번에 추가할 때는 AddRange가 더 효율적입니다.
AddRange는 추가할 갯수를 미리 확인해서 Capacity를 한 번만 확장하고 Array.Copy로 복사하지만, Add를 반복하면 Capacity 체크가 매번 발생하고 Resize가 여러 번 일어날 수 있기 때문입니다.
List<Vector3> positions = new List<Vector3>(1) { Vector3.zero };
// CS1612 컴파일 에러 발생
positions[0].x = 10;
Vector3 temp = positions[0];
temp.x = 10;
positions[0] = temp;TrimExcess() : 내부 배열을 현재 갯수에 맞춰 재할당ArrayList arrayList = new ArrayList();
arrayList.Add(10);
arrayList.Add("Hello World");
System.Collections⇒ 이런 문제 때문에 List 등장
LinkedList<int> linkedList = new LinkedList<int>();
System.Collections.GenericCache Miss : 캐시 효율 낮음Next와 Previous의 이중 연결 리스트(Double Linked List)node.Next 가 head라면 null 반환AddFirst, AddLast, AddBefore, AddAfter : O(1)RemoveFirst, RemoveLast, Remove(node) : O(1) / Remove(value) : O(N)Find(value), FindLast(value), Contains(value) : O(N)| 메모리 주소 | 크기 | 변수 이름 / 역할 | 저장된 값 | |
|---|---|---|---|---|
| 0x1000 | 8 Byte | Object Header | (시스템 정보) | |
| 0x1008 | 8 | MethodTable Pointer | (타입 정보 주소) | |
| 0x1010 | 8 | head | 0x2000 | 첫 노드 주소 |
| 0x1018 | 4 | count | 3 | 데이터 갯수 |
| 0x101C | 4 | version | 3 | 변경 횟수 |
| 메모리 주소 | 크기 | 변수 이름 / 역할 | 저장된 값 | |
|---|---|---|---|---|
| 0x2000 | 8 Byte | Object Header | (시스템 정보) | |
| 0x2008 | 8 | MethodTable Pointer | (타입 정보 주소) | |
| 0x2010 | 8 | list | 0x1000 | 속한 본체 주소 |
| 0x2018 | 8 | next | 0x3000 | 다음 노드 주소 |
| 0x2020 | 8 | prev | 0x4000 | 이전 노드 주소 |
| 0x2028 | 4 | item | 10 | 데이터 |
| 0x202C | 4 | padding | (빈 공간) | 8 바이트 정렬용 |