전체 페이지뷰

2016년 12월 21일 수요일

C# Collection, Indexer

.NET 프레임워크는 배열 말고도 많은 데이터 모음에 대한 자료구조를 제공합니다.
그 중에서  ArrayList, Queue, Stack, Hashtable 에 대해서 간략히 알아보겠습니다.
(제너릭에 해당하는 List 등은 다음에 알아보겠습니다. 제너릭은 정해진 형식만을 취급할 수 있고 지금 말하는 형식은 Object에 해당하여 아무 형식이나 다 가능합니다.)

System.Collections 네임스페이스를 사용합니다.



ArrayList


배열과 유사하나 다른 점이라 하면 크기를 미리 선언할 필요가 없다는 점입니다.

ArrayList list = new ArrayList();
와 같이 선언합니다.

속성으로
Count : 실제 속해 있는 요소의 개수

메소드로는
Add(obj) : 리스트의 끝에 요소를 추가
Insert(index, obj) : 인덱스 위치에 obj 삽입
RemoveAt(index) : 인덱스 위치 요소 삭제 
등이 있습니다.(참고)

사용법을 예로 들어 보겠습니다.

using System;
using System.Collections;
namespace UsingArrayList
    class MainApp
    {
        static void Main(string[] args)
        {
            // ArrayList 선언 및 값 할당
            ArrayList list = new ArrayList();
            for (int i = 0; i < 5; i++)
                list.Add(i);
            // ArrayList 값 출력
            foreach (object obj in list)
                Console.Write("{0} ", obj);
            Console.WriteLine();
            // Insert
            list.Insert(2"A");
            foreach (object obj in list)
                Console.Write("{0} ", obj);
            Console.WriteLine();
            //RemoveAt
            list.RemoveAt(2);
            foreach (object obj in list)
                Console.Write("{0} ", obj);
            Console.WriteLine();
            //Add
            list.Add("Last");
            foreach (object obj in list)
                Console.Write("{0} ", obj);
            Console.WriteLine();
        }
    }
}
cs

보시다시피 모든 요소가 Object입니다. 따라서 요소에 접근할 때마다 박싱 언박싱이 이루어져 프로그램이 커질수록 상당한 부담으로 작용합니다. 나중에 살펴볼 제너릭에서는 형이 정해져 있으므로 부담이 줄어듭니다.

Queue


FIFO(First In First Out)의 특징을 나타내는 자료구조입니다.
많이 쓰이는 속성으로 

Count  :  자료의 개수를 나타냅니다

메소드로
Enqueue() : 개체를 큐의 끝 부분에 추가합니다
Dequeue() : 개체를 큐의 맨 앞에서 제거합니다.

using System;
using System.Collections;
namespace UsingQueue
    class MainApp
    {
        static void Main(string[] args)
        {
            // Queue 생성
            Queue q = new Queue();
            // Enqueue
            q.Enqueue(1);
            q.Enqueue(2);
            q.Enqueue(3);
            q.Enqueue(4);
            q.Enqueue(5);
            // Dequeue
            while (q.Count > 0)
                Console.WriteLine(q.Dequeue());
        }
    }
}
cs

Stack


LIFO(Last In First Out)의 특징을 나타내는 자료구조입니다.
Push()를 이용하여 자료를 넣고 Pop() 메소드로 자료를 꺼냅니다.

Stack stack = new Stack();
stack.Push(1); 
stack.Push(2);
stack.Push(3);   //최상위에 3
Console.WriteLine(stack.Pop()); // 3 출력
cs


Hashtable


키(Key)와 값(Value)의 쌍으로 이루어진 자료구조입니다.

Hashtable ht = new Hashtable();
ht["마징가"= "제트";
ht["태권"= "브이";
Console.WriteLine( ht["마징가"] );  // "제트" 출력
Console.WriteLine( ht["태권"] );  // "브이" 출력
cs



인덱서(Indexer)


인덱서는 클래스나 구조체의 인스턴스를 배열의 요소처럼 접근할 수 있게 해 줍니다.

그 형식은

public int this[ int index ]   //인덱서 선언
{
    // get,set 이용
}

예제를 통해 구체적인 사용법을 알아 보겠습니다.

using System;
using System.Collections;
namespace Indexer
{
    class SampleCollection
    {
        // 요소를 저장하려는 배열 선언
        private int[] arr = new int[10];
        // 인덱서 정의        
        public int this[int i]
        {
            get
            {
                return arr[i];
            }
            set
            {
                arr[i] = value;
            }
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            
            SampleCollection stringCollection = new SampleCollection();
            //  stringCollection 인스턴스에 대해 [] 사용가능
            for (int i = 0; i < 10; i++)
                stringCollection[i] = i;
            for (int i = 0; i < 10; i++)
                Console.WriteLine(stringCollection[i]);
        }
    }
}
cs


IEnumerator, IEnumerable


우리는 foreach문을 이용해 배열이나 리스트 같은 컬렉션에 접근하여 순회 사용할 수 있습니다.  배열이나 리스트가 아닌 인덱서 같은 컬렉션을 foreach 를 이용하여 접근하게 하려면 IEnumerator와 IEnumerable 인터페이스를 상속시키면 됩니다.

IEnumerable  인터페이스에는 GetEnumerator() 메소드만 구현하면 사용가능합니다.
그리고 이 GetEnumerator 메소드는 IEnumerator 인터페이스를 상속하는 클래스의 객체를 반환해야 합니다.

그리고 IEnumerator는 Current라는 속성과 MoveNext(), Reset() 의 두 메소드를 구현해야 합니다.

using System;
using System.Collections;
namespace Enumerator
{
    class SampleCollection:IEnumerable,IEnumerator
    {
        private int[] arr;
        int pos = -1//컬렉션의 위치를 나타냅니다. 0보다 앞인 -1에 초기화합니다.
        public SampleCollection()
        {
            arr = new int[5];
        }
        // 인덱서 정의        
        public int this[int i]
        {
            get
            {
                return arr[i];
            }
            set
            {
                if (i >= arr.Length)
                {
                    Array.Resize<int>(ref arr, i + 1);
                    Console.WriteLine("Array 크기변경: {0}", arr.Length);
                }
                arr[i] = value;
            }
        }     
        
        // IEnumerator의 Current  속성 구현  
        public object Current
        {
            get { return arr[pos]; }
        }
        // IEnumerator의  MoveNext 구현
        public bool MoveNext()
        {
            if (pos == arr.Length - 1)
            {
                Reset();
                return false;
            }
            pos++;
            return (pos < arr.Length);
        }
        // IEnumerator의 Reset 구현
        public void Reset()
        {
            pos = -1;
        }
        // IEnumerable의 GetEnumerator 구현
        public IEnumerator GetEnumerator()
        {
            for (int i = 0; i < arr.Length; i++)
                yield return (arr[i]);
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            
            SampleCollection stringCollection = new SampleCollection();
            //  stringCollection 인스턴스에 대해 [] 사용가능
            for (int i = 0; i < 10; i++)
                stringCollection[i] = i;
            foreach (int e in stringCollection)
                Console.WriteLine(e);
        }
    }
}
cs

댓글 없음:

댓글 쓰기