[This is C#] Chapter 10. 배열과 컬렉션 그리고 인덱서
Table of Contents
이것이 C#이다 책을 읽고 공부한 노트입니다.
배열 #
- 배열을 선언하는 방법
// 요소가 5개인 int형 배열
int[] arr = new int[5];
System.Index
형식과^
연산자^
연산자는 컬렉션의 마지막부터 연순으로 인덱스를 지정하는 기능을 갖고 있다.- 따라서
^1
은 컬렉션의 마지막요소를 나타내는 인덱스이다. Length - 1
이라고 생각하면 쉽다.
// 배열의 마지막 요소에 접근하기
System.Index last = ^1;
arr[last] = 5;
// 이렇게 간결하게 할 수도 있다.
arr[^1] = 5;
배열을 초기화하는 방법 세 가지 #
- 배열을 초기화하는 방법 세 가지
string[] arr1 = new string[3] { "Hi", "Hello", "What's up" };
string[] arr2 = new string[] { "Hi", "Hello", "What's up" };
string[] arr3 = { "Hi", "Hello", "What's up" };
System.Array #
- .NET의 CTS에서 배열은
System.Array
에서 파생된 것이다.- 따라서
System.Array
클래스에 있는 메소드와 프로퍼티를 이용할 수 있다.
- 따라서
private static void Print(int val)
{
Console.WriteLine($"{val} ");
}
private static bool CheckPassed(int score)
{
return score >= 60;
}
static void Main(string[] args)
{
int[] scores = new int[] {90, 80, 70, 60, 50};
// 정렬하기
Array.Sort(scores);
// 출력하기
//Array.ForEach<int>(scores, new Action<int>(Print));
// 차원
int rank = scores.Rank;
// 길이
int length = scores.Length;
// 이진 탐색하기
int bs = Array.BinarySearch<int>(scores, 80);
// 모든 요소가 조건에 부합하는지
bool isTrue = Array.TrueForAll<int>(scores, CheckPassed);
// 조건에 부합하는 첫 번째 요소의 인덱스 찾기
int findIdx = Array.FindIndex<int>(scores, (score) => score < 60);
// 길이 변경하기
Array.Resize<int>(ref scores, 10);
// 숫자는 0으로, 논리는 false로, 참조는 null로 초기화하기
// 3번째 인덱스부터 7개 요소를 초기화한다.
Array.Clear(scores, 3, 7);
// 복사하기
// scores의 0번째 인덱스부터 3개 요소를 sliced의 0번째 인덱스를 시작으로 복사한다.
int[] sliced = new int[3];
Array.Copy(scores, 0, sliced, 0, 3);
}
배열 분할하기 #
System.Range
객체를 사용하면 배열을 쉽게 분할할 수 있다.시작 인덱스..마지막 인덱스
- 마지막 인덱스는 포함하지 않는다.
int[] arr = new int[6];
// 배열 분할하기
System.Range range = 0..4; // 0~3 인덱스
int[] sliced1 = arr[range];
// 이렇게 간결하게 할 수도 있다.
int[] sliced2 = arr[0..4];
// 생략하면 첫 시작과 끝을 의미한다.
int[] sliced3 = arr[..]; // 0~5 전체
int[] sliced4 = arr[..4]; // 0~3
int[] sliced5 = arr[2..]; // 2~5
// ^연산자를 사용할수도 있다.
int[] sliced6 = arr[..^3]; // 0~3
2차원 배열 #
- 2차원 배열
// 2차원 배열의 선언
int[,] arr2d = new int[2, 3]; // 2행 3열
arr2d[0, 2] = 1; // 1행 3열
// 2차원 배열의 초기화
int[,] arr2d1 = new int[2, 3] { { 1, 2, 3 }, { 4, 5, 6 } };
int[,] arr2d2 = new int[,] { { 1, 2, 3 }, { 4, 5, 6 } };
int[,] arr2d3 = { { 1, 2, 3 }, { 4, 5, 6 } };
다차원 배열 #
- 3차원 배열
- 왠만하면 쓰지 않는 것이 좋다.
int[,,] arr3d = new int[4, 3, 2]
{
{ {1, 2}, {3, 4}, {5, 6} },
{ {1, 2}, {3, 4}, {5, 6} },
{ {1, 2}, {3, 4}, {5, 6} },
{ {1, 2}, {3, 4}, {5, 6} }
};
가변 배열 #
- 가변 배열(Jagged Array)
- 다양한 길이의 배열을 요소로 갖는 다차원 배열이다.
- ‘Jagged’란 ‘들쭉날쭉한’을 의미한다.
int[][] jagged = new int[3][];
jagged[0] = new int[5] {1, 2, 3, 4, 5};
jagged[1] = new int[3] {10, 20, 30};
jagged[2] = new int[2] {100, 200};
string[][] jagged2 = new string[2][]
{
new string[3] {"Hello", "Hi", "What's up"},
new string[2] {"Good", "Bye"}
};
컬렉션 맛보기 #
-
컬렉션(Collection)
- .NET이 제공하는 데이터를 담는 자료구조이다.
ICollection
인터페이스를 상속해야한다.
-
ArrayList
는 다양한 형식을 객체를 담을 수 있다.- 이것은 매개변수로
object
형식을 받고 있기 때문이다. - 하지만 박싱과 언박싱은 오버헤드가 많아지므로 피하는 것이 좋다.
- 해결방법은? 나중에 일반화 컬렉션에서 찾아보도록 하자.
- 이것은 매개변수로
-
컬렉션 초기화 방법
ArrayList
,Stack
,Queue
는 배열로 초기화할 수 있다.ArrayList
는 컬렉션 초기자를 사용해서 초기화할 수 있다.Hashtable
은 컬렉션 초기자나 딕셔너리 초기자를 사용해서 초기화할 수 있다.- 컬렉션 초기자를 사용하기 위해서는
IEnumerable
인터페이스와Add()
메소드를 구현해야한다.Stack
과Queue
는Add()
메소드를 구현하지 않기 때문에 컬렉션 초기자를 사용하지 못한다.
int[] arr = {1, 2, 3};
// 배열을 이용한 초기화
ArrayList list = new ArrayList(arr);
Stack stack = new Stack(arr);
Queue queue = new Queue(arr);
// 컬렉션 초기자를 이용한 초기화
ArrayList list2 = new ArrayList() {1, 2, 3};
Hashtable hashtable = new Hashtable() { { "하나", 1 }, { "둘", 2 } , { "셋", 3 } };
// 딕셔너리 초기자를 이용한 초기화
Hashtable hashtable2 = new Hashtable()
{
["하나"] = 1,
["둘"] = 2,
["셋"] = 3
};
인덱서 #
- 인덱서(Indexer)
- 인덱스를 이용해서 객체 내의 데이터에 접근하게 해주는 프로퍼티이다.
class MyList
{
private int[] arr = new int[3];
// 인덱서
public int this[int idx]
{
get
{
return arr[idx];
}
set
{
// 사이즈 보다 인덱스가 크면 늘린 후 저장한다.
if (idx >= arr.Length)
Array.Resize<int>(ref arr, idx + 1);
arr[idx] = value;
}
}
}
class MainApp
{
static void Main(string[] args)
{
MyList m = new MyList();
for (int i = 0; i < 5; i++)
m[i] = i;
for (int i = 0; i < 5; i++)
Console.WriteLine($"{m[i]}"); // 0, 1, 2, 3, 4
}
}
foreach가 가능한 객체 만들기 #
foreach
가 가능하기 위해서는IEnumerable
을 상속해야한다.- 이 인터페이스는
IEnumerator GetEnumerator()
라는 메소드 하나만 갖고 있다.yield
문을 이용하면 따로 구현하지 않아도 컴파일러가 자동으로 처리해준다.
- 이 인터페이스는
class MyList : IEnumerable
{
private int[] arr = new int[3] {0, 1, 2};
public IEnumerator GetEnumerator()
{
yield return arr[0];
yield return arr[1];
yield return arr[2];
yield break;
yield return arr[3]; // 이 코드는 실행되지 않는다.
}
}
class MainApp
{
static void Main(string[] args)
{
MyList m = new MyList();
foreach (int number in m)
Console.WriteLine($"{ number }"); // 0, 1, 2
}
}
-
yield
문 없이IEnumerator
를 직접 구현해보자. -
IEnumerator
의 메소드 및 프로퍼티
메소드 및 프로퍼티 | 설명 |
---|---|
bool MoveNext() |
다음 요소로 이동한다. 컬렉션의 끝을 넘어선 경우에는 false , 이동이 성공한 경우에는 true 를 반환한다. |
void Reset() |
컬렉션의 첫 번째 위치의 ‘앞’으로 이동한다. 첫 번째 위치로 이동하는 것은 MoveNext() 를 호출한 다음에 이루어지므로 -1 이다. |
object Current {get;} |
컬렉션의 현재 요소를 반환한다. |
class MyList : IEnumerable, IEnumerator
{
private int[] arr = new int[3];
private int pos = -1;
// 인덱서
public int this[int idx]
{
get
{
return arr[idx];
}
set
{
if (idx >= arr.Length)
Array.Resize<int>(ref arr, idx + 1);
arr[idx] = value;
}
}
// IEnumerator의 메소드 및 프로퍼티
public object Current { get { return arr[pos]; } }
public bool MoveNext()
{
if (pos == arr.Length - 1)
{
Reset();
return false;
}
pos++;
return (pos < arr.Length);
}
public void Reset()
{
pos = -1;
}
// IEnumerable의 메소드
public IEnumerator GetEnumerator()
{
return this;
}
}
class MainApp
{
static void Main(string[] args)
{
MyList m = new MyList();
for (int i = 0; i < 5; i++)
m[i] = i;
// foreach문이 가능!
foreach (int number in m)
Console.WriteLine($"{ number }"); // 0, 1, 2, 3, 4
}
}