[ 학습 흐름 ]
[ 학습 흐름 ]
- 배열의 초기화
- System.Array
- 2차원 배열
- 다차원 배열
- 가변 배열
- 컬렉션
- 인덱서
[ All For one,one for all ]
[ 배열 ]
//데이터 형식 [] 배열이름 = new 데이터형식[용량];
int[] scores = new int[5];
- 배열은 데이터를 담는 상자이다 . 필요한 용량을 가진 배열을 만든 다음 데이터를 넣을 수 있다.
int[]scores = new int[5];
scores[0]=80;
- 배열의 각 요소에 데이터를 저장할때는 배열이름 뒤에 대괄호를 붙이고 인덱스를 적어준다 .
- 인덱스는 0 부터 시작한다.
using System;
namespace ArraySample;
class MainApp
{
static void Main(string[] args)
{
int[] scores = new int[5];
scores[0] = 80;
scores[1] = 74;
scores[2] = 81;
scores[3] = 90;
scores[4] = 34;
foreach(int score in scores)
{
Console.WriteLine(score);
}
int sum = 0;
foreach(int score in scores)
{
sum += score;
}
//Length는 배열의 용량을 나타낸다
int average=sum/scores.Length;
Console.WriteLine($"Average Score : {average}");
}
}
[ System.Index와 ^연산 ]
System.Index last = ^1
scores[last] = 34 //scores[scores.Length-1]=34와 동일
scores[^1] = 34//더 간소화된 버전
- 배열의 마지막 요소에 접근하려면 배열길이 -1 을 통해 확인해야 하는 번거로움이 있다 .
- ^ 연산자는 컬렉션의 마지막부터 역순으로 인덱스를 지정하는 기능이다 . ex) ^1 =배열.Length-1
- ^연산자의 연산결과는 System.Index 형식의 인스턴스로 나타난다 .
[ 배열을 초기화 하는 세가지 방법 ]
[ 배열 용량을 명시 ]
string[] array1 = new string[3]{"안녕","Hello","Halo"};
- 배열의 원소개수를 명시하고 , 중괄호로 둘어싸인 블록을 붙인 뒤, 블록 사이 각 배열의 원소에 입력될 데이터 입력
- 배열 객체를 초기화하는 {} 블록을 일컬어 컬렉션 초기자라고 부른다 .
[ 배열 용량을 생략 ]
string[] array2 = new string[]{"안녕","Hello","Halo"};
- 용량을 생략하여 초기화해도 컴파일러는 첫번째 방식과 동일한 실행파일을 만든다 .
[ new연산자/형식/대괄호/용량 생략 ]
string[] array3 = {"안녕","Hello","Halo"};
- new 연산자 , 형식 ,대괄호,용량 모두 생략한다 . 첫번째 ,두번째 방식과 동일한 실행파일을 만든다 .
- 세번째 방식이 편하지만 , 코드를 공유해야할때는 상대방이 읽기 편하게 첫번째 방식을 쓰는게 좋다 .
using System;
namespace InitializingArray;
class MainApp
{
static void Main(string[] args)
{
string[] array1 = new string[3] { "안녕", "Hello", "Halo" };
Console.WriteLine("array1..");
foreach (string s in array1) Console.WriteLine($" {s}");
string[] array2 = new string[] { "안녕", "Hello", "Halo" };
Console.WriteLine("array2..");
foreach (string s in array2) Console.WriteLine($" {s}");
string[] array3 = { "안녕", "Hello", "Halo" };
Console.WriteLine("array3..");
foreach (string s in array3) Console.WriteLine($" {s}");
}
}
[ System.Array ]
[ System.Array ]
- .Net의 CTS에서 배열은 System.Array 클래스에 대응된다 .
class MainApp
{
static void Main(string[] args)
{
int[] array = new int[] { 10, 30, 20, 7, 1 };
Console.WriteLine($"Type Of Array : {array.GetType()}");
Console.WriteLine($"Base type Of Array : {array.GetType().BaseType}");
}
}
- 위 예시와 같이 배열이 System.Array 형식에서 파생됐음을 보여준다 .
- System.Array의 특성과 메소드를 파악하면 배열의 특성과 메소드를 알 수 있다 .
[ System.Array의 주요 메소드와 프로퍼티 ]
분류 | 이름 | 설명 |
정적메소드 | Sort() | 배열을 정렬한다 . |
BinarySearch<T>() | 이진 탐색을 수행한다 . | |
IndexOf() | 배열에서 찾고자 하는 특정 데이터의 인덱스를 반환 | |
TrueForAll<T>() | 배열의 모든 요소가 지정한 조건에 부합하는지 여부 반환 | |
FindIndex<T>() | 배열에서 지정한 조건에 부합하는 첫번째 요소의 인덱스 반환 | |
Resize<T>() | 배열의 크기를 재조정한다 | |
Clear() | 배열의 모든 요소를 초기화한다 . 숫자형식이면 0 , 논리기반은 false,참조형식 기반은 null로 초기화한다 , |
|
Foreach<T>() | 배열의 모든 요소에 동일한 작업을 진행한다 . | |
Copy<T>() | 배열의 일부를 다른 배열에 복사한다 . | |
인스턴스 메소드 |
GetLength() | 배열에서 지정한 차원의 길이를 반환한다, |
프로퍼티 | Length | 배열의 길이를 반환한다 |
Rank | 배열의 차원을 반한한다 . |
- <T>를 형식 매개변수라 한다 .
- 호출시 T 대신 배열의 기반 자료형을 인수로 입력하면 컴파일러가 해당 형식에 맞춰 동작하도록 메소드를 컴파일한다 .
using System;
namespace MoreOnArray;
class MainApp
{
static bool CheckPassed(int score)
{
return score >= 60;
}
static void Print(int value)
{
Console.Write($"{value} ");
}
static void Main(string[] args)
{
int[] scores = new int[] { 80, 74, 81, 90, 34 };
foreach (int score in scores)
{
Console.WriteLine(score);
}
Console.WriteLine();
//오름차순으로 정렬
Array.Sort(scores);
Array.ForEach<int>(scores, new Action<int>(Print));
Console.WriteLine();
Console.WriteLine($"Number Of Dimensions : {scores.Rank}");
Console.WriteLine($"Binary Search : 81 is at" + $"{Array.BinarySearch<int>(scores, 81)}");
Console.WriteLine($"Linear Search : 81 is at" + $"{Array.IndexOf(scores, 90)}");
//배열과 함께 조건을 검사하는 메서드를 매개변수로 받는다 .
Console.WriteLine($"Every One Passed ? " + $"{Array.TrueForAll<int>(scores, CheckPassed)}");
//특정조건에 부합하는 메소드를 매개변수로 받는다 . 람다식으로 구현됨
int index = Array.FindIndex<int>(scores, (score) => score < 60);
scores[index] = 61;
Console.WriteLine($"Every One Passed ? " + $"{Array.TrueForAll<int>(scores, CheckPassed)}");
Console.WriteLine($"Old length of Scores : {scores.GetLength(0)}");
Array.Resize<int>(ref scores, 10);
Console.WriteLine($"New length of Scores : {scores.Length}");
Array.ForEach<int>(scores, new Action<int>(Print));
Console.WriteLine();
//3번째 부터 7개를 0으로 초기화
Array.Clear(scores, 3, 7);
Array.ForEach<int>(scores, new Action<int>(Print));
Console.WriteLine();
//배열의 일부를 다른 곳에 복사하는 것을 분할한다고 한다 .
int[] sliced = new int[3];
Array.Copy(scores,0, sliced,0,3);
Array.ForEach<int>(scores, new Action<int>(Print));
Console.WriteLine();
}
}
[ System.Range ]
[ 배열 분할 ]
System.Range r1=0..3//시작인덱스 .. 마지막인덱스
int[]sliced=scores[r1];//인덱스 대신 Range 객체를 입력하면 분할된 배열이 반환된다 .
int[]sliced2 = scores[1..3];// 직접 입력시 간결한 코드
- System.Range는 시작 인덱스와 마지막 인덱스를 이용해서 범위를 나타낸다 .
- System.Range 객체를 생성할떄는 .. 연산자를 이용한다 .
- 주의점은 마지막 인덱스는 배열 분할 결과에서 제외된다 .
int [] sliced3 = scores[..3]//첫번째 요소 (0)부터 세번째 요소 (2)까지
int [] sliced4 = scores[1..]//두번째 요소부터 마지막 요소까지
int[] sliced5 = scores[..]//전체
- .. 연산자가 받아들이는 두 연산자는 생략 가능하다 .
using System;
namespace MoreOnArray;
class MainApp
{
static void PrintArray(System.Array array)
{
foreach(var e in array)
Console.Write(e);
Console.WriteLine();
}
static void Main(string[] args)
{
//대문자 알파벳 개수 26 개
char[] array = new char['Z' - 'A' + 1];
for(int i = 0; i < array.Length; i++)
array[i] = (char)('A'+i);
PrintArray(array[..]);
PrintArray(array[5..]);
Range range_5_10 = 5..10;
PrintArray(array[range_5_10]);
Index last = ^0;
Range range_5_last = 5 ..last;//Range 생성시 리터럴 과 Index객체를 함께 사용 가능
PrintArray(array[range_5_last]);
PrintArray(array[^4..^1]);
}
}
[ 2차원 배열 ]
[ 2차원배열 ]
데이터 형식[,]배열이름 = new 데이터 형식[2차원길이,1차원길이];
int [,]array=new int[2,3]
int[,] arr1 = new int[2,3]{{1,2,3},{4,5,6}};//배열의 형식과 길이를 명시
int[,] arr2 = new int[,]{{1,2,3},{4,5,6}};//배열의 길이를 생략
int[,] arr3 = {{1,2,3},{4,5,6}};//형식과 길이를 모두 생략
- 1차원 배열을 원소로 갖는 배열
- int[2,3]은 기반 형식이 int이며 길이는 3인 1차원 배열을 원소로 2개 갖고 있는 2차원 배열 .
using System;
namespace _2DArray;
class MainApp
{
static void Main(string[] args)
{
int[,] arr = new int[2, 3] { { 1, 2, 3 }, { 4, 5, 6 } };
for(int i = 0; i < arr.GetLength(0); i++)
{
for(int j = 0; j < arr.GetLength(1); j++)
{
Console.Write($"[{i},{j}] : {arr[i, j]} ");
}
Console.WriteLine();
}
}
}
[ 다차원 배열 ]
[ 다차원배열 ]
int[,,]array=new int[4,3,2]
{
{{1,2},{3,4},{5,6}},
{{1,4},{2,5},{3,6}},
{{6,5},{4,3},{2,1}},
{{6,3},{5,2},{4,1}},
}
- 차원이 둘 이상인 배열
- 3차원 이상의 배열은 추천하지 않는다 .
- 복잡한 구조의 배열을 선언시 만들려는 배열의 각 크기를 지정해주는 것이 좋다 .
using System;
namespace _3DArray;
class MainApp
{
static void Main(string[] args)
{
int[,,] arr = 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}},
};
for(int i = 0; i < arr.GetLength(0); i++)
{
for(int j = 0; j < arr.GetLength(1); j++)
{
Console.Write("{ ");
for(int k = 0; k < arr.GetLength(2); k++)
{
Console.Write($"{arr[i, j,k]}");
}
Console.Write(" }");
}
Console.WriteLine();
}
}
}
[ 가변 배열 ]
[ 가변 배열 ]
- 다양한 길이의 배열을 요소로 갖는 다차원 배열로 이용 될 수 있다 .
- 가변 배열은 다차원 배열과 다르게 배열을 요소로 사용해 접근할 수 있다 .
- 영어로는 Jagged Array (들쭉날쭉한)라 칭한다 .
데이터형식[][] 배열이름 = new 데이터 형식[가변배열의 용량][];
//가변 배열의 요소로 입력되는 배열은 그 길이가 모두 같을 필요가 없다.
int[][]jagged = new int[3][];
jagged[0]=new int[5]{1,2,3,4,5};
jagged[1]=new int[]{10,20,30};
jagged[2]=new int[]{100,200};
- 가변 배열의 요소로 입력되는 배열은 그 길이가 같을 필요가 없다 .
int[][] jagged2 = new int[2][]{new int[]{1000,2000},new int[4]{6,7,8,9}};
- 가변배열도 선언과 동시에 초기화가 가능하다 .
- 가변 배열의 요소는 "배열이다"
using System;
namespace _3DArray;
class MainApp
{
static void Main(string[] args)
{
int[][] jagged = new int[3][];
jagged[0] = new int[5] { 1, 2, 3, 4, 5 };
jagged[1] = new int[] { 10, 20, 30 };
jagged[2] = new int[] { 100, 200 };
foreach (int[]arr in jagged)
{
Console.Write($"Length : {arr.Length}, ");
foreach(var e in arr)
Console.Write($"{e} ");
Console.WriteLine("");
}
}
}
[ 컬렉션 ]
[ 컬렉션 ]
- 같은 성격을 띈 데이터의 모음을 담는 자료구조
- 컬렉션 자료구조는 Iclollection 인터페이스를 상속한다 .
[ Array List ]
- 컬렉션의 요소에 접근할때 [] 연산자 이용
- 특정 위치에 있는 요소에 데이터 임의 할당이 가능
- 배열과 다르게 용량을미리 지정할 필요 없이 자동으로 늘어나거나 줄어든다 .
using System;
using System.Collections;
namespace UsingList;
class MainApp
{
static void Main(string[] args)
{
ArrayList list = new ArrayList();
for(int i=0;i<5;i++)
list.Add(i);
foreach (object obj in list)
Console.Write($"{obj} ");
Console.WriteLine();
list.RemoveAt(2);
foreach (object obj in list)
Console.Write($"{obj} ");
Console.WriteLine();
list.Insert(2, 2);
foreach (object obj in list)
Console.Write($"{obj} ");
Console.WriteLine();
list.Add("abc");
list.Add("def");
for (int i = 0; i < list.Count; i++)
Console.Write($"{list[i]}");
Console.WriteLine();
}
}
- Add 메서드는 컬렉션의 마지막에 있는 요소 뒤에 새 요소를 추가한다
- RemoveAt 메서드는 특정 인덱스에 있는 요소를 제거한다
- Insert 메서드는 원하는 위치에 새 요소를 삽입한다 .
- Add / Insert 메서드는 매개변수로 obj 형식을 받기에 다양한 형식 객체를 담지만 박싱,언박싱이 이루어진다 .
- 박싱/언박싱은 적지 않은 오버헤드가 발생하기에 데이터가 많을수록 성능저하가 일어난다 .
[ Queue ]
- 데이터를 차례대로 입력했다 입력된 순서대로 하나씩 꺼내 처리하기 위해 사용한다 .
- 입력은 오직 뒤에서 , 출력은 앞에서만 이루어진다 .
using System;
using System.Collections;
namespace UsingQueue;
class MainApp
{
static void Main(string[] args)
{
Queue que = new Queue();
que.Enqueue(1);
que.Enqueue(2);
que.Enqueue(3);
que.Enqueue(4);
que.Enqueue(5);
while(que.Count > 0);
Console.WriteLine(que.Dequeue());
}
}
[ Stack ]
- 먼저 들어온 데이터가 나중에 나가는 선입후출의 구조이다 .
using System;
using System.Collections;
namespace UsingStack;
class MainApp
{
static void Main(string[] args)
{
Stack stack = new Stack();
stack.Push(1);
stack.Push(2);
stack.Push(3);
stack.Push(4);
stack.Push(5);
while(stack.Count > 0);
Console.WriteLine(stack.Pop());
}
}
[ HashTable ]
- 키와 값 쌍으로 이루어진 데이터를 다룰때 사용한다
- 어떠한 형식이든 키로 사용 가능하다
- 배열만큼 빠른 탐색속도를 가지고 있다 .(해싱)
using System;
using System.Collections;
namespace UsingHashTable;
class MainApp
{
static void Main(string[] args)
{
Hashtable ht = new Hashtable();
ht["하나"] = "One";
ht["둘"] = "Two";
ht["셋"] = "Three";
ht["넷"] = "Four";
ht["다섯"] = "Five";
Console.WriteLine(ht["하나"]);
Console.WriteLine(ht["둘"]);
Console.WriteLine(ht["셋"]);
Console.WriteLine(ht["넷"]);
Console.WriteLine(ht["다섯"]);
}
}
[ 컬렉션 초기화 하는 방법 ]
[ 배열의 도움 ]
int arr={123,456,789};
ArrayList list = new ArrayList(arr);
Queue queue = new Queue(arr);
Stack stack = new Stack(arr);
ArrayList list2 = new ArrayList(){11,22,33}
- ArrayList / Queue / Stack은 배열의 도움을 받아 간단하게 초기화를 수행할수있다.
- ArrayList는 배열의 도움 없이 직접 컬렉션 초기자를 이용하여 초기화 할 수 있다 .
- 컬렉션 초기자는 IEnumerable 인터페이스의 Add 메서드를 구현하는 컬렉션만 지원하기에 ArrayList만 사용 가능하다 .
[ HashTable 초기화 ]
HashTable ht = new HashTable()
{
["하나"]=1,
["둘"]=2
};
- 딕셔너리 초기자를 이용한다 .
HashTable ht = new HashTable()
{
{"하나",1},
{"둘",2}
};
- 컬렉션 초기자 역시 이용 가능하다 .
[ 인덱서 ]
[ 인덱서 ]
class 클래스 이름
{
한정자 인덱서형식 this[형식 index]
{
get
{
//index를 이용하여 내부 데이터 변환
}
set
{
//index를 이용하여 내부 데이터 저장
}
}
}
- 인덱스를 이용해서 객체 내의 데이터에 접근하게 해주는 프로퍼티 (객체를 배열처럼 사용하게 해준다)
- 인덱서는 인덱스를 통해 객체 내 데이터에 접근하기에 식별자를 따로 가지지 않는다 .
using System;
using System.Collections;
namespace Indexer;
class MyList
{
private int[] array;
public MyList()
{
array=new int[3];
}
public int this[int index]
{
get { return array[index]; }
set
{
if(index>=array.Length)
{
Array.Resize(ref array, index+1);
Console.WriteLine($"Array Resized : {array.Length}");
}
array[index] = value;
}
}
public int Length
{
get { return array.Length; }
}
}
class MainApp
{
static void Main(string[] args)
{
MyList list = new MyList();
for (int i = 0; i < 5; i++)
list[i] = i;
for(int i=0;i<list.Length;i++)
Console.WriteLine(list[i]);
}
}
[ foreach가 가능한 객체 만들기 ]
[ foreach문의 조건 ]
- foreach 구문은 IEnumerable을 상속하는 형식만 지원한다 .
- 해당 인터페이스는 GetEnumerator()메서드 하나만 가지고 있다 .
- GetEnumerator()는 IEnumerator 인터페이스를 상속하는 클래스의 객체를 반환해야 한다 .
[ yield문의 이용 ]
- yield 문을 이용하면 컴파일러가 자동으로 IEnumerator를 구현한 클래스를 생성해준다 .
- yield return 문은 현재 메소드의 실행을 일시 정지하고 호출자에게 결과를 반환한다
- 메소드가 다시 호출되면 일시 정지된 실행을 복구 , yield return/yield break문을 만날때까지 작업을 실행한다 .
using System;
using System.Collections;
namespace Yield;
class MyEnumerator
{
int[] numbers = { 1, 2, 3, 4 };
public IEnumerator GetEnumerator()
{
yield return numbers [0];
yield return numbers[1];
yield return numbers[2];
yield break;
yield return numbers[3];
}
}
class MainApp
{
static void Main(string[] args)
{
var obj=new MyEnumerator();
foreach(int i in obj)
Console.WriteLine(i);
}
}
[ IEnumerator 객체의 이용 ]
메소드 또는 프로퍼티 | 설명 |
boolean MoveNexet() | 다음 요소로 이동한다.컬렉션의 끝을 지난 경우 false , 이동이 성공한 경우 true를 반환한다 |
void Reset() | 컬렉션의 첫번째 위치의 "앞"으로 이동한다 .첫번째 위치가 0번인 경우 Reset()을 호출하면 -1번으로 이동하는 것. 첫번째 위치로의 이동은 MoveNext()를 호출한 다음 이뤄진다 . |
Object Current{get;} | 컬렉션의 현재 요소를 반환한다 . |
- IEnumerator 인터페이스의 메소드 및 프로퍼티 목록이다 .
-MoveNext()/Reset()/Current 프로퍼티의 구현시 IEnumerator의 조건을 충족한다 .
- 모두 충족한 클래스는 IEnumerable이 요구하는 GetIEnumerator()메소드 구현시 자기 자신을 반환하면 된다 .
using System;
using System.Collections;
namespace Enumerable;
class MyList : IEnumerable, IEnumerator
{
int[] array;
//컬렉션의 현재 위치를 다루는 변수.초깃값은 0이 아닌 -1.
//foreach문이 첫번째 반복을 수행하면 MoveNext메서드를 실행하고
//이때 Position이 1이 되어 두번째 요소를 가져오는 문제가 발생하기 때문
int position = -1;
public MyList()
{
array = new int[3];
}
public int this[int index]
{
get { return array[index]; }
set
{
if (array.Length <= index)
{
Array.Resize(ref array, index+1);
Console.WriteLine($"Array Resized : {array.Length}");
}
array[index] = value;
}
}
public object Current
{
get { return array[position]; }
}
public IEnumerator GetEnumerator()
{
Console.WriteLine("GetEnumerator");
return this;
}
public bool MoveNext()
{
Console.WriteLine("MoveNext");
if(position==array.Length-1)
{
Reset();
return false;
}
position++;
return position < array.Length;
}
public void Reset()
{
Console.WriteLine("Reset");
position = -1;
}
}
class MainApp
{
static void Main(string[] args)
{
MyList list=new MyList();
for (int i = 0; i < 5; i++)
list[i]=i;
foreach(int e in list)
Console.WriteLine(e);
}
}
'C# > 이것이 C#이다' 카테고리의 다른 글
[ 이것이 C#이다 ] Chapter 12 . 예외처리 (0) | 2024.03.20 |
---|---|
[ 이것이 C#이다 ] Chapter 11 . 일반화 프로그래밍 (0) | 2024.03.18 |
[ 이것이 C#이다 ] Chapter 09 . 프로퍼티 (0) | 2024.03.12 |
[ 이것이 C#이다 ] Chapter 08 . 인터페이스와 추상 클래스 (2) | 2024.03.12 |
[ 이것이 C#이다 ] Chapter 07 . 클래스 (0) | 2024.03.06 |