Skip to main content

[This is C#] Chapter 10. 배열과 컬렉션 그리고 인덱서

이것이 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()메소드를 구현해야한다. StackQueueAdd()메소드를 구현하지 않기 때문에 컬렉션 초기자를 사용하지 못한다.
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
    }
}