NGMsoftware

NGMsoftware
로그인 회원가입
  • 매뉴얼
  • 학습
  • 매뉴얼

    학습


    C# 21-2. 배열과 컬렉션. (Array, Collection)

    페이지 정보

    본문

    안녕하세요. 소심비형입니다. 오늘은 저번 시간에 이어서 배열과 컬렉션에 대해 좀 더 자세하게 알아보겠습니다. 사실 이외에도 더 많은 부분이 있지만, 모두 다루기에는 무리가 있습니다. 아직 배우지도 않은 익명 함수라던가 링큐, 제네릭등등... 이런 것을과 조합하여 사용할 때 엄청난 시너지를 내게 됩니다.

     

    아래 예제는 대리자와 제네릭 대리자가 사용되어 있지만, 이 부분은 크게 중요하지 않으므로 자세한 설명은 하지 않습니다. 코드가 좀 길어보이긴 하지만, 여러가지 테스트가 포함되어 있으므로 한번씩 확인하고 넘어가야 합니다.

    Program.cs

    using System;
    namespace ArrayTest
    {
        class MainApp
        {
            private static bool CheckPassed(int score)
            {
                return score >= 60;
            }
     
            private static void Print(int value)
            {
                Console.Write($"{value} ");
            }
     
            static void Main(string[] args)
            {
                int[] scores = new int[] { 80, 74, 81, 90, 42 };
     
                foreach (int score in scores)
                    Console.Write($"{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 : 90 is at {Array.IndexOf(scores, 90)}");
                Console.WriteLine($"Everyone passed ? : {Array.TrueForAll<int>(scores, CheckPassed)}");
     
                int index = Array.FindIndex<int>(scores, delegate (int score)
                { return score < 60; });
     
                scores[index] = 61;
                Console.WriteLine($"Everyone 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();
     
                Array.Clear(scores, 3, 7);
                Array.ForEach<int>(scores, new Action<int>(Print));
                Console.WriteLine();
                Console.ReadKey();
            }
        }
    }

     

     

    위 코드의 25라인에서 배열 안에 있는 요소를 정렬합니다. 기본적으로 오름차순으로 정렬되며, 내림차순으로 정렬하려면 Sort한 후 Array.Reverse를 이용합니다. 보통은 배열의 요소를 정렬함에 있어 IComparer를 구현합니다.

    IComparer를 구현하는 이유는 내가 원하는데로 정렬이 안되기 때문입니다. 대부분의 Third-party제품들도 내부적으로 정렬 기능을 가지고 있지만, 제가 사용해본 제품들은 모두 원하는 정렬 기능을 제공하지 못했습니다. 간단하게 예를 들어보면, 1차원 문자열 배열 요소에 인덱스를 할당했을 때 발생됩니다. 이런 경우에 정렬하면 정상적으로 Sort가 되지 않습니다. IComparer에 대해서 다룰 때 좀 더 자세하게 알아보겠습니다.

     

    36~40라인은 요소를 반복하면서 60보다 작은 값이 발견될 때 요소의 위치를 반환하는 예제입니다. 대리자가 사용되어 있기 때문에 이해하기 어려울수도 있습니다. 배열 요소에서 특정한 인덱스를 찾고자 할 때 주로 사용됩니다. 배열은 선언할 때 배열의 길이가 결정되지만, 47라인처럼 Resize를 통해 길이를 늘리거나 줄일 수 있습니다. 54라인처럼 지정된 위치의 모든 요소를 쉽게 초기화 할 수 있습니다. 

     

    위 코드를 실행한 결과는 아래와 같습니다.

    LpXKguM.png

     

     

    아래 예제는 가변 배열을 사용하는 방법입니다.

    Program.cs

    using System;
    namespace ArrayTest
    {
        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 (int e in arr)
                    {
                        Console.Write($"{e} ");
                    }
     
                    Console.WriteLine();
                }
     
                Console.WriteLine();
     
                int[][] jagged2 = new int[2][] {
                    new int[] { 1000, 2000 },
                    new int[4] { 6, 7, 8, 9 } };
     
                foreach (int[] arr in jagged2)
                {
                    Console.Write($"Length : {arr.Length}, ");
     
                    foreach (int e in arr)
                    {
                        Console.Write($"{e} ");
                    }
     
                    Console.WriteLine();
                }
     
                Console.ReadKey();
            }
        }
    }
    

     

     

    위 코드의 경우 그리 특별한 내용은 없습니다. 이미 우리는 다차원 배열과 초기화에 대해서 배웠기 때문입니다. 하지만, 이전 시간에 배운 내용과는 약간의 차이가 존재합니다. 개념의 차이이긴 한데, 가변 배열이라는 용어는 단순히 말해 배열 안에 요소가 다양한 크기를 가진다는 의미입니다. 따라서 배열의 요소에 길이가 정해지지 않은 배열을 담는다는 뜻입니다. 가변 배열은 배열의 배열이라고도 부릅니다.

    위 코드를 실행하면 아래와 같은 결과를 확인할 수 있습니다.

    jQdEHUb.png

     

     

    배열에 대한 내용은 이만 줄이고, 이제부터 컬렉션에 대해 알아보겠습니다. 사실 업무 분야에 따라 다르겠지만, 제가 일하는 곳은 대부분 컬렉션을 사용하고 있습니다. 만약, 단순 배열을 이용하여 데이타를 저장해야 한다면 구조체를 사용하는게 더 좋습니다.

     

    배열과 컬렉션은 어떤 차이점을 가지고 있을까요?

    배열은 동일한 형식을 가진 개체들을 모아 놓은 것입니다. 배열의 길이에는 거의 제한이 없으므로 수천 또는 수백만 개의 개체를 저장하는데 배열을 사용할 수 있지만, 그 크기는 배열을 만들 때 결정됩니다. 배열의 각 항목에는 색인을 통해 엑세스할 수 있습니다. 색인은 배열에서 개체가 저장된 위치 또는 슬롯을 나타내는 숫자입니다.

    배열과 컬렉션은 데이타 집합을 저장하는 여러가지 방법중 하나일뿐입니다. 컬렉션은 시작 위치나 중간에 삽입하려는 경우 일반적으로 목록을 사용하는 것이 배열을 사용하는 것보다 빠릅니다. 컬렉션 클래스의 다른 형식으로는 맵, 트리 및 스택이 있으며 각 형식마다 나름의 장점이 있습니다. 또한, 형식 안전성을 위해 제네릭 컬렉션을 주로 사용하며, 대표 타입을 이용하면 일반화 프로그래밍이 가능해집니다.

     

    일반화 프로그래밍에 대한 내용은 아래 링크를 읽어보시면 좋겠네요.

    18. 제네릭을 이용한 버튼 구성과 일반화 프로그래밍 구현.

     

    이제 IEnumerableIEnumerator 인터페이스를 이용하여 제네릭이 아닌 단순한 컬렉션을 직접 구현해 보겠습니다. 코드량이 많기 때문에 어려워 보일수도 있겠지만, 상속 받은 각각의 인터페이스를 따로 분석한다면 크게 어려운 부분은 없을겁니다.

    Program.cs

    using System;
    using System.Collections;
     
    namespace Enumerable
    {
        class MyList : IEnumerable, IEnumerator
        {
            private int[] array; int position = -1; public MyList()
            {
                array = new int[3];
            }
            public int this[int index]
            {
                get { return array[index]; }
                set
                {
                    if (index >= array.Length)
                    {
                        Array.Resize<int>(ref array, index + 1);
                        Console.WriteLine("Array Resized : {0}", array.Length);
                    }
     
                    array[index] = value;
                }
            }
            // IEnumerator 멤버        
            public object Current { get { return array[position]; } }
            // IEnumerator 멤버        
            public bool MoveNext()
            {
                if (position == array.Length - 1)
                {
                    Reset();
                    return false;
                }
     
                position++;
     
                return (position < array.Length);
            }
            // IEnumerator 멤버        
            public void Reset() { position = -1; }
            // IEnumerable 멤버        
            public IEnumerator GetEnumerator()
            {
                for (int i = 0; i < array.Length; i++)
                {
                    yield return (array[i]);
                }
            }
        }
     
        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);
            }
        }
    }
    

     

     

    위 코드의 16라인은 인덱서(Indexer)입니다. 인덱서는 다음 시간에 알아볼 내용이므로 여기에서는 넘어가도록 하겠습니다. 아래 결과를 확인한 후 이전 시간에 알아본 배열과 비교 해보세요.

    dsnvgM9.png

     

     

    아래 알아볼 해시태이블은 키의 해시 코드에 따라 구성된 키/값 쌍의 컬렉션입니다.

    Program.cs

    using System;
    using System.Collections;
     
    namespace CollectionTest
    {
        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["다섯"]);
            }
        }
    }

     

     

    리스트는 어떤 값이라도 담을 수 있는 컬렉션을 제공합니다. Array와 유사하지만, 형식이 정해져 있는 배열과는 차이가 있습니다.

    Program.cs

    using System;
    using System.Collections;
     
    namespace CollectionTest
    {
        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("{0} ", obj);
     
                Console.WriteLine();
     
                list.RemoveAt(2);
     
                foreach (object obj in list)
                    Console.Write("{0} ", obj);
     
                Console.WriteLine();
     
                list.Insert(2, 2);
     
                foreach (object obj in list)
                    Console.Write("{0} ", obj);
     
                Console.WriteLine();
     
                list.Add("abc");
                list.Add("def");
     
                for (int i = 0; i < list.Count; i++)
                    Console.Write("{0} ", list[i]);
     
                Console.WriteLine();
            }
        }
    }
    

     

     

    이제 잘 사용되지 않는 Queue와 Stack이 남았네요. 먼저 Queue에 대해서 간단한 예제를 확인 해보겠습니다. Queue(큐)는 요소의 선입선출(FIFO: First In First Out) 컬렉션입니다.

    Program.cs

    using System;
    using System.Collections;
     
    namespace CollectionTest
    {
        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());
            }
        }
    }

     

     

    먼저 들어간 요소가 가장 먼저 나옵니다.

    Ryv8wiJ.png

     

     

    Stack은 Queue와 반대로 후입선출(LIFO) 방식의 컬렉션입니다.

    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());
     
                Console.ReadLine();
            }
        }
    }
    

     

     

    후입선출이 되고 있는것을 확인할 수 있습니다.

    rSoJuVl.png

     

     

    다음 시간에...

    • 네이버 공유하기
    • 페이스북 공유하기
    • 트위터 공유하기
    • 카카오스토리 공유하기
    추천0 비추천0

    댓글목록

    등록된 댓글이 없습니다.