ch10 배열과 컬렉션, 그리고 인덱서
챕터 10 '배열과 컬렉션, 그리고 인덱서'입니다
우리가 지금까지 다뤄왔던 변수를 명함이라 한다면 배열이나 컬렉션은 명함집이라 할 수 있습니다.
프로그래머의 데이터 관리 고민을 덜어주는 배열을 배워봅시다.
'이것이 C#이다' 교재를 바탕으로 정리했습니다.
이전 정리 글
2020/01/06 - [이것이 C#이다./이것이 C# 이다. 책 정리] - Ch01 프로그래밍을 시작합시다.
2020/01/06 - [이것이 C# 이다./이것이 C# 이다. 책정리] - Ch02 처음 만드는 C# 프로그램
2020/01/06 - [이것이 C# 이다./이것이 C# 이다. 책정리] - Ch03 데이터 보관하기
2020/01/07 - [이것이 C#이다./이것이 C# 이다. 책 정리] - 부록A. 문자열 다루기
2020/01/07 - [이것이 C# 이다./이것이 C# 이다. 책정리] - Ch04. 데이터를 가공하는 연산자
2020/01/08 - [이것이 C# 이다./이것이 C# 이다. 책정리] - Ch05 코드의 흐름 제어하기
2020/01/09 - [이것이 C#이다./이것이 C# 이다. 책 정리] - ch06 메소드로 코드 간추리기
2020/01/11 - [이것이 C# 이다./이것이 C# 이다. 책정리] - ch07 클래스
2020/01/17 - [이것이 C# 이다./이것이 C# 이다. 책정리] - ch08 인터페이스와 추상 클래스
ㅇ All for one, one for All |
프로그램을 작성하다 보면 같은 성격을 띤 다수의 데이터를 한 번에 다뤄야 하는 경우가 자주 생깁니다.
int score_1 = 53;
int score_2 = 12;
int score_3 = 64;
int score_4 = 81;
int score_5 = 70;
예시로 다섯 명의 학생을 위한 다섯 개의 변수를 선언했고, 여기에 다섯 명의 점수를 입력했습니다.
하지만 학생수가 50명 60명 일 경우에는 어떻게 해야 될까요? 45개의 변수를 추가해야 될까요?
이럴 때 필요한 게 "배열"입니다.
50개 60개의 용량을 가진 변수 "한 개"만 선언하면 되니깐요.
배열의 선언 형식은 다음과 같습니다.
데이터 형식[] 배열이름 = new 데이터형식[ 용량 ];
//예를 들어 5개인 int 형식의 배열은 다음과 같습니다.
int [] array = new int [5];
배열의 각 요소에 데이터를 저장하거나, 요소 안에 있는 데이터를 읽어올 때에는
배열 이름 뒤에 괄호 [], 괄호 사이에 인덱스를 적어주면 됩니다.
주의하실 점은 인덱스는 1부터가 아닌 0부터 시작합니다.
int [] score = new int [5]
score [0] = 10;
score [1] = 20;
score [2] = 30;
score [3] = 40;
score [4] = 50;
배열은 for문과 foreach 문과 같은 반복문과 잘 어울립니다.
예제와 같이 보겠습니다.
이용한 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
using System;
using static System.Console;
namespace ch9_1
{
class Ex9_1
{
static void Main(string[] args)
{
int[] scores = new int[5];
scores[0] = 80; // 배열 인덱스에 값 대입
scores[1] = 74; //
scores[2] = 65; //
scores[3] = 90; //
scores[4] = 66; //
foreach (int score in scores) // scroes 배열을 순회하여
// 한개한개 scroe에 넣어줌
{
WriteLine(score); // score에 내용물 출력
}
int sum = 0; // 합계가 저장될 변수
foreach (int score in scores)
{
sum += score; // sum = sum + score 과 동일
}
int average = sum / scores.Length; // Length는 배열의 길이를 나타내줌
WriteLine($"Average Score : {average}"); // 평균(average) 값 출력
}
}
}
|
ㅇ 배열을 초기화하는 방법 세 가지 |
첫 번째
배열의 원소 개수를 명시하고,
그 뒤에 괄호 { }로 둘러싸인 블록을 붙인 뒤,
블록 사이에 배열의 각 원소에 입력될 데이터 입력
string [ ] array1 = new string[ 3 ]{ "안녕", "Hello", "Halo" };
두 번째
첫 번째 방법과 비슷하지만, 배열의 용량을 생략하는 것입니다.
컴파일러는 첫 번째 방법을 이용해서 초기화한 것과 동일한 실행 파일을 만들어냅니다.
string [ ] array2 = new string[ ]{ "안녕", "Hello", "Halo" };
세 번째
new 연산자, 형식과 괄호 [ ], 배열의 용량을 모두 생략한 채
코드 블록 사이에 배열의 각 원소에 할당할 데이터를 넣어줍니다.
string[ ] array3 ={ "안녕","Hello","Halo" };
ㅇ 알아두면 삶이 윤택해지는 System.Array |
C#에서는 모든 것이 객체입니다. 배열도 객체이며, 당연히 기반이 되는 형식이 있습니다.
배열은 System.Array 클래스에 대응됩니다.
Array 클래스 메서드는 다음과 같습니다.
분류 | 이름 | 설명 |
정적 메소드 | Sort() | 배열을 정렬합니다. |
BinarySearch<T>() | 이진 탐색을 수행합니다. | |
IndexOf() | 배열에서 찾고자 하는 특정 데이터의 인덱스를 반환합니다. | |
TrueForAll<T>() | 배열의 모든 요소가 지정한 조건에 부합하는지의 여부를 반환합니다. | |
FindIndex<T>() |
배열에서 지정한 조건에 부합하는 첫 번째 요소의 인덱스를 반환합니다. IndexOf() 메서드가 특정 값을 찾는데 비해, FindIndex<T>() 메소드는 지정한 조건에 바탕하여 값을 찾습니다. |
|
Resize<T>() | 배열의 크기를 재조정합니다. | |
Clear() | 배열의 모든 요소를 초기화합니다. 배열이 숫자 형식 기반이면 0으로, 논리 형식 기반이면 false로, 참조 형식 기반이면 null로 초기화합니다. | |
ForEach<T>() | 배열의 모든 요소에 대해 동일한 작업을 수행하게 됩니다. |
인스턴스 메소드 | GetLength() | 배열에서 지정한 차원의 길이를 반환합니다. |
프로퍼티 | Length | 배열의 길이를 반환합니다. |
Rank | 배열의 차원을 반환합니다. |
표에 언급한 모든 메소드 이용한 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
using System;
using static System.Console;
namespace p328
{
class Program
{
private static bool CheckPassed(int score) // score 점수가 통과인지 실패인지 확인하는 메소드
{
if (score >= 60) // 60이상이면 통과
return true;
else
return false; // 아니면 실패 값 반환
}
private static void Print(int value) // 점수를 출력해줄 Print 메소드
{
Write($"{value} ");
}
static void Main(string[] args)
{
int[] scores = new int[] { 91, 85, 90, 61, 34 }; // 크기 5인 배열 선언
foreach (int score in scores) // scores를 순회하여 각 데이터가 score에 담김
Write($"{score} "); // score가 출력
WriteLine(); // 한줄 띄움
Array.Sort(scores); // Sort는 배열을 오름차순 정리해줌
Array.ForEach<int>(scores, new Action<int>(Print)); // 위에 foreach랑 동일한 작업 수행
// ForEach를 통해 모든 요소에 동일한 작업을 수행하게 함
// Action은 델리게이트 변수
// 뒤에 있는 작업을 수행시킴 Print => 클래스 수행
WriteLine();
WriteLine($"Number of dimensions : {scores.Rank}"); // 배열 차원값 반환
WriteLine("Binary Search : 81 is at {0}",
Array.BinarySearch<int>(scores, 90)); // BinarySearch는 이진검색트리이다.
// 사용하기 전에 Sort(정렬)되어 있어야된다.
// 90의 위치값이 반환되며 만약 찾는 값이 없다면
// 음수가 반환되고 그 값이 들어가는 위치가 반환된다.
WriteLine("Linear Search : 90 is at {0}",
Array.IndexOf(scores, 85)); // 특정 값을 찾고 그 인덱스 값을 반환한다.
WriteLine($"Everyone Passed ? :" +
$"{Array.TrueForAll<int>(scores, CheckPassed)}"); // CheckPassed 클래스를 통해 그 값이 부합한지 판단합니다.
// 여기선 34가 부합하지 않으므로 False 출력
int index = Array.FindIndex<int>(scores, // 특정 조건을 가지고 요소를 찾고 그 인덱스를 반환합니다.
// 여기선 34가 부합하므로 34의 인덱스 값인 4가 반환
delegate (int score)
{
if (score < 60)
return true;
else
return false;
});
scores[index] = 61; // 34자리에 61 값을 새로 넣음
WriteLine($"Everyone passed ? :" +
$"{Array.TrueForAll<int>(scores, CheckPassed)}"); // 결국 모든 요소가 부합하므로 True 출력
WriteLine($"Old length of scroes : {scores.GetLength(0)}"); // GetLength메소드의 0은 1차원, 1은 2차원을 뜻함
// 각 차원의 요소 수를 반환 즉 길이 반환
Array.Resize<int>(ref scores, 10); // 배열의 길이를 10으로 늘림
WriteLine($"New Length of scroes : {scores.GetLength(0)}"); // 처음에는 5였지만 10으로 늘렸기 때문에 10 출력
Array.ForEach<int>(scores, new Action<int>(Print)); // Print 클래스를 수행. 배열의 각 요소를 출력
WriteLine();
Array.Clear(scores, 3, 7); // socres배열의 인덱스 3~7번째의 값을 초기화 시킴
// 표에 설명되어 있지만 숫자형식은 0으로 초기화
Array.ForEach<int>(scores, new Action<int>(Print)); // 다시 Print 클래스 수행. 각 요소를 출력
WriteLine();
}
}
}
|
ㅇ 2차원 배열 |
2차원 배열은 2개의 차원(세로+가로)으로 원소를 배치하는 2차원 배열입니다.
1차원 배열을 원소로 갖는 배열이라고 할 수 있습니다.
선언 방식은
데이터 형식[ , ] 배열 이름 = new 데이터 형식[2차원 길이, 1차원 길이];
차원의 용량 또는 길이를 콤마( , )로 구분한다는 점이 기본 1차원 배열과 다릅니다.
int [ , ] array = new int[ 2, 3 ];
array [0,0] = 1;
array [0,1] = 2;
array [0,2] = 3;
array [1,0] = 4;
array [1,1] = 5;
array [1,2] = 6;
ㅇ 가변 배열 |
가변 배열은 "배열을 요소로 갖는 배열"입니다.
조금 오해할 수 있지만 배열의 길이를 늘였다 줄였다 할 수 있는 배열을 떠올리기 쉽지만
"들쭉 날쭉한 배열"로 생각하는 게 좋습니다.
가변 배열은 다음과 같이 선언합니다.
데이터 형식[ ][ ] 배열 이름 = new 데이터 형식[가변 배열의 용량][ ];
위에 들쭉 날쭉한 배열의 설명과 같이
가변 배열의 요소로 입력되는 배열은 그 길이가 모두 같을 필요가 없습니다.
int [ ][ ] jagged = new int [3][ ];
jagged [0] = new int [5] {1,2,3,4,5}; // 0번 요소에는 길이가 5인 배열
jagged [1] = new int [] {10,20,30}; // 1번 요소에는 길이가 3인 배열
jagged [2] = new int [] {100,200}; // 2번 요소에는 길이가 2인 배열
ㅇ 컬렉션 맛보기 |
컬렉션이란, 같은 성격을 띠는 데이터의 모음을 담는 자료 구조를 말합니다.
배열과 비슷하죠? 배열도. NET 프레임워크가 제공하는 다양한 컬렉션 자료 구조의 일부입니다.
우선은 크게 4가지의 자료 구조를 알아보고 특징과 사용 방법만 간단히 설명하겠습니다.
ArrayList
ArrayList는 가장 배열과 닮은 컬렉션입니다.
컬렉션의 요소에 접근할 때는 [ ] 연산자를 이용하고 특정 위치에 있는 요소에 데이터를 임의로 할당할 수도 있습니다.
배열과 차이점은
컬렉션을 생성할 때 용량을 미리 지정할 필요가 없이 필요에 따라 자동으로 그 용량이 늘어나거나 줄어듭니다.
ArrayList의 가장 중요한 메서드는
Add(), RemoveAt(), Insert()
이렇게 세 개입니다.
Add()는 컬렉션의 가장 마지막 요소 뒤에 새 요소를 추가하고
RemoveAt()는 특정 인덱스에 있는 요소를 제거하고
Insert()는 원하는 위치에 새 요소를 삽입합니다.
예제로 바로 알아보겠습니다.
예제 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
using System;
using System.Collections;
using static System.Console;
namespace p351
{
class MainApp
{
static void Main(string[] args)
{
ArrayList list = new ArrayList(); // list 생성
for (int i = 0; i < 5; i++)
{
list.Add(i); //for문을 이용하여 Add 메소드로 i의 값을 리스트에 저장
}
foreach (object obj in list) // int형으로 생성해도 됨.
{
Write($"{obj} "); //
}
WriteLine();
list.RemoveAt(2); // 2번째 요소를 제거함
// 0 1 2 3 4 << 중에 "2"번 삭제
foreach (object obj in list)
{
Write($"{obj} "); // 다시 출력해보면
// 0 1 3 4
}
WriteLine();
list.Insert(2, 2); // 2번째 인덱스에 2 삽입
foreach (object obj in list)
{
Write($"{obj} "); // 0 1 2 3 4 출력
}
WriteLine();
list.Add("abc"); // string형 또한 담을 수 있다.
list.Add("def");
for (int i = 0; i < list.Count; i++)
{
Write($"{list[i]} "); // 배열의 요소 출력
}
WriteLine();
}
}
}
|
Queu
Queu 자료 구조는 "선입 선출"의 특성을 지키는 구조입니다.
입력은 오직 뒤에서, 출력은 앞에서만 이루어집니다.
Queue에 데이터를 입력하는 것은 Enqueue() 메서드를 이용합니다.
Queu que = new Queue();
que.Enqueue(1);
que.Enqueue(2);
que.Enqueue(3);
que.Enqueue(4);
Queue에서 데이터를 꺼낼 때는 Dequeue() 메소드를 이용합니다.
주의할 점은 Dequeue를 실행하면 데이터를 자료 구조에서 실제로 꺼내게 된다는 것입니다.
Stack
Stack은 먼저 들어온 데이터가 나중에 나가고,
나중에 들어온 데이터는 먼저 나가는 구조입니다.
Stack에 데이터를 넣을 때는 Push()
데이터를 꺼낼 때는 Pop() 메소드를 이용합니다.
Hashtable
Hashtable은 '키'와 '값'의 쌍으로 이루어진 데이터를 다룰 때 사용합니다.
사전이 가장 좋은 예가 되겠군요.
"book"을 키로, "책"을 값으로 입력하는 식이죠.
탐색 속도가 빠르고, 사용하기도 편합니다.
Hashtable ht = new Hashtable();
ht ["book"] = "책";
ht ["cook"] = "요리사";
ht ["tweek"] = "지저귀다";
Consle.WriteLine(ht ["book"]); // 책
Consle.WriteLine(ht ["cook"]); // 요리사
Consle.WriteLine(ht ["tweek"]); // 지저귀다
배열과 비슷하지만
배열이 데이터를 저장할 요소의 위치를 인덱스로 사용하는 반면,
Hashtable 컬렉션은 키 데이터를 그대로 사용합니다.
또한 어떤 형식이든 키로 사용할 수 있습니다.
int, float, 우리가 만든 클래스도 말입니다.
Hashtable은 배열에서 인덱스를 이용해서 배열 요소에 접근하는 것에 준하는
탐색 속도를 자랑합니다.
다시 말하면 탐색 속도가 거의 소요되지 않는다고 할 수 있습니다.