[이것이 C#이다] 10. 배열,컬렉션,인덱서

100353 단어 CC

Key point

  • 배열
  • System.Array
  • 배열의 종류
  • 컬렉션
  • 인덱서

10.1 배열

10.1.1 배열 선언

  • 데이터를 담는 상자
  • 선언
    데이터형식[] 배열이름 = new 데이터형식[ 용량 ];
    ex) int[] scores = new int[5];  // 용량이 5개인 int 형식의 배열
  • 데이터 저장
    배열이름[ 인덱스 ] =;
    ex) scores[0] = 80;  // scores배열의 첫번째 값 저장 (인덱스 0부터 시작)
  • 배열은 for문이나 foreach문과 함께 사용하면 코드를 훨씬 더 간결하게 만들어준다.

예제 프로그램

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;

            int average = sum / scores.Length;  // Length 프로퍼티 : 배열의 용량

            Console.WriteLine($"Average Score : {average}");
        }
    }
}

10.1.2 배열 인덱스 & System.Index

  • 배열에 접근할 때 대괄호와 인덱스 이용
   int[] scores = new int[5];
   scores[scores.Length-1] = 34;  // scores[4] = 34;
   // 인덱스가 0부터 시작하므로 마지막 요소에 접근할 때는 1을 빼주어야 함
  • System.Index형식과 ^연산자를 이용하면 보다 쉽게 배열에 접근할 수 있다.
    • ^연산자 : 컬렉션의 마지막부터 역순으로 인덱스를 지정하는 기능
   // System.Index의 인스턴스 생성
   System.Index last = ^1; // ^1 = 배열의 마지막을 나타내는 인덱스
   scores[last] = 34; // scores[scores.Length-1] = 34;
   
   // 더 간결한 버전
   scores[^1] = 34;  // scores[scores.Length-1] = 34;

예제 프로그램

using System;

namespace ArraySample2
{
    class MainApp
    {
        static void Main(string[] args)
        {
            int[] scores = new int[5];
            scores[0] = 80;
            scores[1] = 74;
            scores[2] = 81;
            scores[^2] = 90;  // 배열 뒤에서 두번째
            scores[^1] = 34;  // 배열 뒤에서 첫번째

            foreach (int score in scores)
                Console.WriteLine(score);

            int sum = 0;
            foreach (int score in scores)
                sum += score;

            int average = sum / scores.Length;

            Console.WriteLine($"Average Score : {average}");
        }
    }
}

10.1.3 배열 초기화

  • 컬렉션 초기자(Collection Initializer) : 배열 객체를 초기화하는 {} 블록

예제 프로그램

using System;

namespace InitializingArray
{
    class MainApp
    {
        static void Main(string[] args)
        {
        	// 방법1 : 배열 형식, 용량/길이 명시
            string[] array1 = new string[3] { "안녕", "Hello", "Halo" };

            Console.WriteLine("array1...");
            foreach (string greeting in array1)
                Console.WriteLine($" {greeting}");
			
            // 방법2 : 배열 형식만 명시 (용량/길이 생략)
            string[] array2 = new string[] { "안녕", "Hello", "Halo" };

            Console.WriteLine("\narray2...");
            foreach (string greeting in array2)
                Console.WriteLine($" {greeting}");
			
            // 방법3 : 배열 형식, 용량/길이 모두 생략
            string[] array3 = { "안녕", "Hello", "Halo" };

            Console.WriteLine("\narray3...");
            foreach (string greeting in array3)
                Console.WriteLine($" {greeting}");
        }
    }
}



10.2 System.Array

10.2.1 System.Array 형식

  • C#에서는 모든 것이 객체로 배열은 .NET의 CTS(Common Type System)에서 System.Array 형식을 기반으로 파생되었다.

    using System;
    
    namespace DerivedFromArray
    {
       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}");
           }
       }
    }

10.2.2 Array 클래스의 주요 메소드, 프로퍼티

  • <T>
    • 형식 매개변수(Type Parameter)
    • T 대신 배열의 기반 자료형을 인수로 입력하면 컴파일러가 해당 형식에 맞춰 동작하도록 메소드를 컴파일함

예제 프로그램

using System;

namespace MoreOnArray
{
    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, 34};
			
            // foreach문
            foreach (int score in scores)
                Console.Write($"{score} ");
            Console.WriteLine();
			
            // 정렬
            Array.Sort(scores);
            // 모든 요소에 동일한 작업 수행
            Array.ForEach<int>(scores, new Action<int>(Print)); // <int> : 배열 기반 자료형
            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)}");
                // CheckPassed : TrueForAll 메소드는 배열과 함께 '조건을 검사하는 메소드'를 매개변수로 받음
			
            // 지정 조건에 부합하는 첫 번째 요소의 인덱스 반환
            int index = Array.FindIndex<int>(scores, (score) => score < 60);
            // 람다식 : FindIndex 메소드는 `특정 조건에 부합하는 메소드`를 매개변수로 받음 
			
            // 데이터 저장 및 변경
            scores[index] = 61;
            
            // 모든 요소가 지정한 조건에 부합하는지 여부 반환
            Console.WriteLine($"Everyone passed ? : " +
                $"{Array.TrueForAll<int>(scores, CheckPassed)}");
			
            // 지정한 차원의 길이 반환 (다차원 배열에서 유용하게 사용)
            Console.WriteLine("Old length of scores : " +
                $"{scores.GetLength(0)}");
            
            // 크기 재조정 : 배열 용량 10으로 재조정
            Array.Resize<int>(ref scores, 10);
            
            // 길이/용량 반환
            Console.WriteLine($"New length of scores : {scores.Length}");
			
            // 모든 요소에 동일한 작업 수행
            Array.ForEach<int>(scores, new Action<int>(Print));
            Console.WriteLine();
			
            // 모든 요소 초기화
            // 숫자 형식 → 0 | 논리 형식 → false | 참조 형식 → null
            Array.Clear(scores, 3, 7); // 인덱스 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>(sliced, new Action<int>(Print));
            Console.WriteLine();
        }        
    }
}

10.3 배열 분할 : System.Range

  • ..연산자를 활용해 시작 인덱스와 마지막 인덱스를 이용해서 범위를 나타내는 방법

    • ..연산자 의 두번째 피연산자, 즉 마지막 인덱스는 배열 분할 결과에서 제외된다. (시작 연산자는 포함)

    • [..]처럼 시작과 마지막 인덱스를 모두 생략하면 배열 전체를 나타내는 System.Range 객체를 반환한다.

      // 첫 번째(0) ~ 세 번째(2) 요소
      int[] sliced3 = scores[..3];
      
      // 두 번째(1) ~ 마지막 요소
      int[] sliced4 = scores[1..];
      
      // 전체
      int[] sliced5 = scores[..];
    • System.Index 객체를 이용할 수도 있다.

      System.Index idx = ^1;
      int[] sliced5 = scores[..idx];
      
      int[] sliced6 = scores[..^1];  // ^연산자를 직접 입력하면 코드가 더 간결해짐

예제 프로그램

using System;

namespace Slice
{
    class MainApp
    {
        static void PrintArray(System.Array array)
        {
            foreach (var e in array)
                Console.Write(e);
            Console.WriteLine();
        }

        static void Main(string[] args)
        {
            char[] array = new char['Z'-'A'+1]; // 90-65+1 = 26
            // Z의 ASCII 코드 = 90, A의 ASCII 코드 = 65
            for (int i = 0; i < array.Length; i++)
                array[i] = (char)('A'+i);  // array에 A부터 Z까지 입력

            PrintArray(array[..]);    // 0번째부터 마지막까지
            PrintArray(array[5..]);   // 5번째부터 끝까지

            Range range_5_10 = 5..10;
            PrintArray(array[range_5_10]); // 5번째부터 9(10-1)번째까지      

            Index last = ^0; // 마지막 인덱스
            Range range_5_last = 5..last;  // Range 생성시 리터럴과 Index 객체를 함께 사용할 수 있다.
            PrintArray(array[range_5_last]); // 5번째부터 끝(^)까지      

            PrintArray(array[^4..^1]);  // 끝에서 4번째부터 끝(^)에서 1번째까지      
        }
    }
}

10.4 2차원 배열

  • 1차원 배열(가로)을 원소로 갖는 배열
  • 2개의 차원(세로+가로)으로 원소를 배치
  • 선언
   데이터형식[,] 배열이름 = new 데이터형식[2차원길이, 1차원길이];
   ex) int[,] array = new int [2, 3];
  • 2차원 배열의 원소 접근
	// 배열이름 [두번째 차원, 첫 번째 차원]
   Console.WriteLine( array[ 0, 2 ] );
   Console.WriteLine( array[ 1, 1 ] );
  • 2차원 배열 선언 + 초기화
   // 배열의 형식과 길이 명시
   int[,] arr = 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}};

예제 프로그램

using System;

namespace InitializingArray
{
    class MainApp
    {
        static void Main(string[] args)
        {
        	// 방법1 : 배열의 형식과 길이 명시 
            string[] array1 = new string[3] { "안녕", "Hello", "Halo" };

            Console.WriteLine("array1...");
            foreach (string greeting in array1)
                Console.WriteLine($" {greeting}");
			
            // 방법2 : 배열의 길이 생략
            string[] array2 = new string[] { "안녕", "Hello", "Halo" };

            Console.WriteLine("\narray2...");
            foreach (string greeting in array2)
                Console.WriteLine($" {greeting}");
			
            //방법3 : 형식과 길이 모두 생략
            string[] array3 = { "안녕", "Hello", "Halo" };

            Console.WriteLine("\narray3...");
            foreach (string greeting in array3)
                Console.WriteLine($" {greeting}");
        }
    }
}



10.5 다차원 배열

  • 차원이 둘 이상인 배열
  • 선언 문법은 2차원 배열의 문법과 같다. 다만 차원이 늘어날수록 요소에 접근할 때 사용하는 인덱스의 수가 2개, 3개, 4개...의 식으로 늘어나는 점이 다르다.
  • 3차원 배열 : 2차원 배열을 요소로 갖는 배열

예제 프로그램

using System;

namespace _3DArray
{
    class MainApp
    {
        static void Main(string[] args)
        {
            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} },
            };

            for (int i = 0; i < array.GetLength(0); i++)
            {
                for (int j = 0; j < array.GetLength(1); j++)
                {
                    Console.Write("{ ");
                    for (int k = 0; k < array.GetLength(2); k++)
                    {
                        Console.Write($"{array[i, j, k]} ");
                    }
                    Console.Write("} ");
                }
                Console.WriteLine();
            }
        }
    }
}



10.6 가변 배열(Jagged Array)

  • 다양한 길이의 배열을 요소로 갖는 다차원 배열
  • 들쭉날쭉한 배열
  • 선언
   데이터형식[][] 배열이름 = new 데이터형식[가변 배열의 용량][];
   ex) int[][] jagged = new int[3][];
  • 가변 배열의 요소로 입력되는 배열은 그 길이가 모두 같을 필요가 없다.
   int[][] jagged = new int[3][];
   jagged[0] = new int[5] { 1, 2, 3, 4, 5 };  // 5
   jagged[1] = new int[] { 10, 20, 30 };  // 3
   jagged[2] = new int[] { 100, 200 };  // 2
  • 가변 배열도 선언과 동시에 초기화가 가능하다.
   int[][] jagged2 = new int[2][] { 
       new int[] { 1000, 2000 }, 
       new int[4] { 6, 7, 8, 9 } };

예제 프로그램

using System;

namespace JaggedArray
{
    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();
            }
        }
    }
}



10.7 컬렉션 맛보기

  • 컬렉션(Collection) : 같은 성격을 띈 데이터의 모음을 담는 자료구조
  • 배열도 .NET이 제공하는 다양한 컬렉션 자료구조의 일부
    /* .NET의 여타 컬렉션들이 무조건 상속해야 하는 ICollection 인터페이스를
       System.Array 클래스가 상속함으로써 자신이 컬렉션 가문의 일원임을 증명하고 있다. */
    
    public abstract class Array : ICloneable, IList, ICollection, IEnumerable

10.7.1 ArrayList

  • 가장 배열과 닮은 컬렉션
  • [] 연산자를 통해 요소 접근
  • 배열과 달리 컬렉션을 생성할 때 용량을 미리 지정할 필요 없이 필요에 따라 자동으로 그 용량이 늘어나거나 줄어든다.
  • 메소드
    • Add() : 가장 마지막에 있는 요소 뒤에 새 요소 추가
    • RemoveAt() : 특정 인덱스에 있는 요소 제거
    • Insert() : 원하는 위치에 새 요소 삽입

예제 프로그램

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

ArrayList가 다양한 형식의 객체를 담을 수 있는 이유

// Add(), Insert() 메소드의 선언
public virtual int Add( Object value )
public virtual void Insert( int index, Object value )

모든 형식은 object를 상속하므로 object 형식으로 간주될 수 있다.
그래서 Add() 메소드에 int형식의 데이터를 넣더라도
정수 형식 그대로 입력되는 것이 아닌 object 형식으로 박싱되어 입력되는 것.
반대로 ArrayList의 요소에 접근해서 사용할 때는
원래의 데이터 형식으로 언박싱이 이루어진다.

여기서 박싱과 언박싱은 오버헤드를 꽤 요구하는데
이러한 성능 저하는 데이터가 많으면 많아질수록 더욱 늘어난다.

10.7.2 Queue

  • 대기열, 즉 기다리는 (대기) 줄(열)
  • 데이터나 작업을 차례대로 입력해뒀다가 입력된 순서대로 하나씩 꺼내 처리하기 위해 사용됨
  • 먼저 들어온 것이 먼저 나가는 "선입선출"로, FIFO(First In First Out)의 구조
  • 연산
    • 삽입 연산 : Enqueue()
    • 삭제 연산 : Dequeue() (데이터를 자료구조에서 실제로 꺼내는 것)

예제 프로그램

using System;
using System.Collections;

namespace UsingQueue
{
    class MainApp
    {
        static void Main(string[] args)
        {	
        	// Queue 선언
            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()); // 삭제
        }
    }
}

10.7.3 Stack

  • "쌓다"라는 의미로, 데이터를 차곡차곡 쌓아 올린 형태의 자료구조
  • 가장 마지막에 삽입된 자료가 가장 먼저 삭제되는 "후입 선출"로 LIFO(Last In First Out)의 구조
  • 연산
    • 삽입 연산 : push()
    • 삭제 연산 : pop()

예제 프로그램

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());  // 삭제
        }
    }
}

10.7.4 Hashtable

  • 키(Key), 값(Value)의 쌍으로 이루어진 데이터를 다룰 때 사용
  • 탐색 속도가 거의 소요되지 않을 정도로 빠름
    • 해싱(Hashing) : 키를 이용해 단번에 데이터가 저장된 컬렉션 내의 주소를 계산해내는 작업

예제 프로그램

using System;
using System.Collections;
using static System.Console;

namespace UsingHashtable
{
    class MainApp
    {
        static void Main(string[] args)
        {
            Hashtable ht = new Hashtable();
            ht["하나"] = "one";
            ht["둘"]   = "two";
            ht["셋"]   = "three";
            ht["넷"]   = "four";
            ht["다섯"] = "five";

            WriteLine( ht["하나"] ) ;
            WriteLine( ht["둘"]   );
            WriteLine( ht["셋"]   );
            WriteLine( ht["넷"]   );
            WriteLine( ht["다섯"] );
        }
    }
}

좋은 웹페이지 즐겨찾기