[C#] 07. 배열과 리스트


Study/C#  2020. 6. 30. 17:16

안녕하세요. 명월입니다.

 

이 글은 C#의 배열과 리스트에 대한 글입니다.

 

배열이란 같은 종류의 데이터를 연속적으로 저장하는 변수 타입입니다. (참고로 연속적으로 데이터를 저장한다는 의미는 논리적 개념입니다. C++와 비교해서 C#은 Stack 메모리 할당이 없기 때문에 실제 메모리에 연속적으로 저장되지 않습니다.)

자료형[] 배열이름 = new 자료형[배열크기];
using System;

namespace Example
{
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // int형 배열 10개 선언
      int[] a = new int[10];
      // 배열의 인덱스는 0부터 시작한다.
      a[0] = 1;
      // 각 배열에 값을 설정한다.
      a[1] = 2;
      a[2] = 3;
      a[3] = 4;
      a[4] = 5;
      a[5] = 6;
      a[6] = 7;
      a[7] = 8;
      a[8] = 9;
      a[9] = 10;
      // 합계 변수
      int sum = 0;
      // 반복문으로 사용해서 0부터 9까지 배열에 값을 합계 변수에 더한다.
      for (int i = 0; i < 10; i++)
      {
        // 합계 변수에 배열의 값을 더한다.
        sum += a[i];
      }
      // 콘솔 출력
      Console.WriteLine("sum = {0}", sum);
      // 아무 키나 누르면 종료
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}

위 예제에서 배열을 10개 선언했습니다. 즉, int 타입의 변수가 10개라는 의미입니다.

배열의 인덱스는 0부터 시작합니다.

 

결과는 하나의 변수명에 10개의 int 데이터를 저장했습니다.

 

위 예제의 배열은 일차원 배열입니다. 즉, 하나의 연속된 데이터라고 생각하면 됩니다.

배열은 일차원만 있는 것이 아니고 다차원 배열도 있습니다.

자료형[ , ] = new 자료형[ , ];
자료형[ , ,] = new 자료형 [ , , ];
자료형[][] = new 자료형[][];
using System;

namespace Example
{
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // 다차원 배열 선언, 5차의 5개, 즉 배열의 개수는 25개
      int[,] a = new int[5, 5];
      // 다차원 배열은 중첩 반복문을 이용할 수 있다.
      for (int i = 0; i < 5; i++)
      {
        for (int j = 0; j < 5; j++)
        {
          // 중첩 배열에 배열의 인덱스의 곱을 넣는다.
          a[i, j] = i * j;
        }
      }
      // 콘솔 출력; 2 * 3 = 6
      Console.WriteLine("a[2, 3] = " + a[2, 3]);
      // 다차원 배열 선언 
      int[][] b = new int[5][];
      // 각 1차원 배열마다 선언되는 배열 수가 다르다.
      // a[0]는 1개, a[1]는 2개, a[2]는 3개, a[3]는 4개, a[4]는 5개
      for (int i = 0; i < 5; i++)
      {
        // 배열 선언
        b[i] = new int[i + 1];
      }
      // 값을 위한 버퍼
      int index = 0;
      // 각 배열에 값을 넣는다.
      for (int i = 0; i < b.Length; i++)
      {
        for (int j = 0; j < b[i].Length; j++)
        {
          // 넣을 때마다 버퍼 증가
          b[i][j] = ++index;
        }
      }
      // 배열 탐색
      for (int i = 0; i < b.Length; i++)
      {
        for (int j = 0; j < b[i].Length; j++)
        {
          // 콘솔 출력
          Console.WriteLine("b[" + i + "][" + j + "] = " + b[i][j]);
        }
      }
      // 아무 키나 누르면 종료
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}

배열은 단순하게 하나의 변수명에 여러개의 데이터를 저장하는 목적으로 사용하는 것은 아닙니다. 데이터가 연속적으로 있다고 생각을 하면, 정렬이나 탐색등으로 사용 가능합니다.

정렬이라는 것은 일련의 숫자를 오름차순 혹은 내림차순으로 정리하는 것입니다.

using System;

namespace Example
{
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // 정렬을 실행할 배열을 받는다.
      // 이 배열을 버블 정렬을 통해 오름차순으로 정렬한다.
      int[] sort = new int[10] { 512, 64, 24, 7, 23, 624, 1, 5, 17, 10 };
      // 정렬 횟수
      int count = 0;
      // 배열의 정렬이 없으면 정렬 끝
      bool check = true;
      // 정렬이 완료될 때까지 반복한다.
      while (true)
      {
        // 배열의 개수만큼 돌린다. (마지막 배열은 정렬이 필요없다.)
        for (int i = 0; i < sort.Length - 1; i++)
        {
          // 배열 비교
          // 예로 sort[0] = 512와 sort[1] = 64가 만났을때,	
          // sort[0]의 값이 크므로 sort[0] = 64, sort[1] = 512로 배열 값을 바꾼다.
          if (sort[i] > sort[i + 1])
          {
            int swap = sort[i];
            sort[i] = sort[i + 1];
            sort[i + 1] = swap;
            // 정렬이 일어나면 재 탐색한다.	
            check = false;
          }
        }
        // 정렬이 일어나지 않으면 정렬된 상태입니다.	
        if (check)
        {
          // 루프 종료
          break;
        }
        else
        {
          // 정렬처리가 일어남	
          count++;
          // 정렬 여부
          check = true;
          // 콘솔 출력
          Console.Write(count + " - sort ");
          // 배열 출력
          foreach (int s in sort)
          {
            // 콘솔 출력
            Console.Write(s + " ");
          }
          // 개행 출력
          Console.WriteLine();
        }
      }
      // 개행 출력
      Console.WriteLine();
      // 배열 출력
      foreach (int s in sort)
      {
        // 콘솔 출력
        Console.Write(s + " ");
      }
      // 개행 출력
      Console.WriteLine();
      // 아무 키나 누르면 종료
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}

버블 정렬을 설명하면 먼저 초기 sort의 변수에 정렬할 배열의 값을 입력합니다. 초기 배열에 입력된 값은 정렬이 된 값이 아닌 무작위 수를 입력했습니다.

여기서 배열 0번째와 다음 차수의 배열과 값을 비교하여 높은 수가 높은 차수로 가게끔 배열 값을 교체합니다. 다시 다음 배열 1번째와 다음 차수 배열과 값을 비교하여 높은 수가 뒤로 가게끔 배열 값을 교체합니다. 이런 식 정렬 계산하는 것이 버블 정렬입니다.


다음의 디버깅 표로 값을 확인하겠습니다.

입력값
512 64 24 7 23 623 1 5 17 10
1차정렬
64 24 7 23 512 1 5 17 10 623
2차정렬
24 7 23 64 1 5 17 10 512 623
3차정렬
7 23 24 1 5 17 10 64 512 623
4차정렬
7 23 1 5 17 10 24 64 512 623
5차정렬
7 1 5 17 10 23 24 64 512 623
6차정렬
1 5 7 10 17 23 24 64 512 623

위 디버깅 표대로 512가 sort[4]까지 이동합니다. 왜냐하면, sort[5]와 비교할 때는 sort[5]의 값이 623의 값이기 때문입니다. 다시 623은 sort[6]과 비교되면서 sort[9]로 이동합니다.

두번째 정렬에서는 64가 sort[3]까지 이동합니다. sort[4]가 512이기 때문입니다. 다시 512는 623보다 작기 때문에 sort[8]로 이동합니다.

그렇게 1차, 2차, 3차 정렬이 이루어지 지면서 최종 6번의 정렬을 거치면서 최종 오름 차순의 버블 정렬이 완성되었습니다.

 

배열이라는 것은 선언시에 사용할 개수를 설정하여 선언합니다.

사양에 따라서는 그 배열의 개수가 정해지지 않고 몇개를 입력할 지는 유저의 입력된 값에 따라 달라지는 경우가 있습니다.

그럴 때는 리스트를 사용합니다.

List<자료형> 변수명 = new List<자료형>();
using System;
using System.Collections.Generic;

namespace Example
{
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // 리스트 선언
      List<int> list = new List<int>();
      // 데이터 입력
      list.Add(1);
      list.Add(2);
      list.Add(3);
      list.Add(4);
      // list의 개수만큼 반복
      for(int i = 0; i < list.Count; i++)
      {
        // 리스트에 입력된 순서로 콘솔 출력
        Console.WriteLine(list[i]);
      }
      // list의 데이터를 foreach 반복문을 이용해서 데이터를 취득
      foreach(var item in list)
      {
        // 리스트에 입력된 순서로 콘솔 출력
        Console.WriteLine(item);
      }
 
      // 아무 키나 누르면 종료
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}

리스트는 선언할 때, 배열처럼 개수를 정하지 않습니다.

즉, 유저가 입력하는 만큼, 계속해서 데이터를 입력할 수 있습니다. 배열의 경우는 정해진 개수를 초과하게 되면 에러가 발생합니다.

여기까지 보면 리스트가 배열보다 많이 편리해 보입니다. 실무에서도 배열보다는 리스트를 많이 사용합니다.

그러나 리스트에는 치명적인 단점이 있습니다. 리스트라는 것은 연결 리스트 알고리즘으로 구성되어 있습니다. 즉, 입력은 빠릅니다만, 탐색이 매우 느린 알고리즘 구조입니다.

즉, 대량의 데이터를 사용할 때는 리스트보다 배열의 경우가 탐색 속도가 빠릅니다. 그러나 사실, 이건 이론적인 이야기입니다. 최근에는 이런 이론적인 이야기를 무시할 만큼, 놀라운 성능의 하드웨어를 사용합니다. 그래서, 실무에서는 그냥 리스트를 사용합니다. 배열을 사용 함으로 괜히 버그를 만들 필요가 없는 것이지요.

그러나 이것도 엄청난 대량의 데이터(약 1억건 이상의 데이터)라고 한다면 리스트가 아닌 배열을 사용하는 것도 생각해 볼 필요가 있습니다.


여기까지 C#의 배열과 리스트에 대한 글이었습니다.

 

궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.