코드스테이츠 10일차

안형준·2022년 5월 9일
0

코드스테이츠

목록 보기
10/32
post-thumbnail

1차 학습 목표

  1. 배열이 무엇인지 이해한다.
  2. 1차원 배열과 다차원 배열의 차이를 이해한다.
  3. 문자열 배열을 이해한다.
  4. 배열을 선언하고 초기화하는 방법을 이해한다.
  5. 배열을 탐색하고 값을 사용하는 방법을 이해한다.
👻1차원 배열
배열은 크게 1차원 배열과 다차원 배열, 이 두 가지로 나눌 수 있다.

배열이 가진 각각의 값을 요소 또는 원소라고 한다.
배열의 요소에 접근할 때에는 요소의 순서를 활용한다.
배열에서 모든 요소는 순서에 따른 번호표를 가지는데 그것을 인덱스라고 한다.

👻배열을 선언하고 초기화 하기
참조 타입 : 변수에 저장될 때, 주소 값이 저장되는 타입이다.
배열을 할당하기 위한 변수는 참조 변수여야 하는데 왜 그럴까?
1. 변수를 선언한다는 것은 메모리 상에 값을 저장할 공간을 확보하고 이름을 붙이는 것이다.
따라서 변수를 선언할 때 컴퓨터에게 얼마 만큼의 공간이 필요한지를 알려주어야 한다.
예를 들어, int age;와 같이 변수를 선언하면 컴퓨터는 데이터 타입이 int형임을 인지하고, 메모리 공간을 4byte만큼 확보한 다음, 그 메모리 공간에 age라는 이름을 붙여준다.
2. 정수형, 실수형, 문자형, 논리형 등의 기본 타입은 정해진 데이터 타입의 크기가 존재하기 때문이다.
따라서 기본 타입의 값을 저장하는 변수는 선언되는 시점에 필요한 메모리 공간의 크기를 컴퓨터가 알 수 있다.
3. 하지만 배열은 크기가 정해져 있지 않다.
어떤 배열은 int형의 요소를 5개 가진 배열일 수도 있고, 어떤 배열은 double형의 요소를 500개 가진 배열일 수도 있으며, 심지어 길이가 제각기 다른 문자열 1000개를 가지는 배열일 수도 있다.
즉, 어떤 타입의 값을 요소로 가지는지에 따라 배열이 차지하는 메모리 공간의 크기는 크게 달라진다.
따라서, 배열을 선언하여 컴퓨터가 메모리 공간을 확보하는 시점에 컴퓨터는 배열의 크기가 몇인지, 즉 얼마 만큼의 공간을 확보해야 하는지 알 수 없다.
결과적으로, 배열이 선언되는 시점에 필요한 메모리 공간의 크기를 알 수 없으므로, 배열을 가리키는 변수는 배열이 생성되고 나서 배열이 위치한 메모리 공간의 주소를 저장하게 되며, 이처럼 값이 위치한 주소를 저장하고 있는 변수를 참조 변수라고 한다.

배열을 생성하고 초기화하는 방법은?
// 선언과 동시에 초기화
int[] arr1 = new int[5]; // arr -> {0, 0, 0, 0, 0}
// 배열의 요소에 값을 지정하지 않으면 각 요소는 해당 타입의 기본값으로 자동으로 채워진다.

// 선언 후 초기화 
int[] arr;
arr = new int[5]; // arr -> {0, 0, 0, 0, 0}
// 배열의 요소에 값을 지정하지 않으면 각 요소는 해당 타입의 기본값으로 자동으로 채워진다.

// 선언과 동시에 값을 넣어 초기화
int[] arr = {1, 2, 3, 4, 5};
int[] arr = new int[] {1, 2, 3, 4, 5}; // arr -> {1, 2, 3, 4, 5}

배열을 생성하기 위해서는 위에서처럼 new 데이터타입[배열크기]를 입력해야 한다.
즉, new int[5]는 “정수형 요소를 5개 갖는 배열을 만들어줘”라는 의미가 되는 것이다.

아래와 같이 배열의 길이와 인덱스를 반복문과 함께 활용할 수 있다.
int arr = new int[10];

// 배열 요소 초기화 : 반복문 완료 후 arr -> {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
for (int i = 0; i < arr.length; i++) {
   arr[i] = i + 1;
}

// 배열 요소 수정 : 반복문 완료 후 arr -> {10, 20, 30, 40, 50, 60, 70, 80, 90, 100}
for (int i = 0; i < arr.length; i++) {
   arr[i] *= 10;
}

👻다차원 배열
배열이 배열의 요소로 들어가 있는 경우를 다차원 배열이라고 한다.

2차원 배열이란?
2차원 배열이란 배열 안에 배열이 중첩된 배열을 의미한다.
즉, 어떤 배열 A가 자신의 요소로 또 다른 배열B, C, D를 가지고 있다면, 배열 A를 2차원 배열이라고 한다.

👻2차원 배열 선언과 초기화
1차원 배열을 선언한 것에 []를 추가하면 2차원 배열을 만들 수 있다.

ex)
// 크기 할당 및 초기화 없이 선언만 하는 경우
int[][] arr;

// 하지만 아래와 같이 2차원 배열을 선언할 수는 없다.
int arr[][];

// 2차원 이상 배열을 만들때는 []만 추가하면 된다.
// 예시로 3차원 배열을 선언하는 경우
int[][][] arr;

위의 예시에서는 참조변수를 할당할 수 있는 공간을 확보한 것뿐이다. 실제로 배열을 생성하고, 변수에 초기화하기 위해서는 아래와 같은 과정이 필요하다.

ex)
// 선언과 동시에 배열의 크기를 지정한 경우
int[][] arr = new int[2][3]; 
// 배열은 아래와 같이 초기화가 된다.
// {
//   {0, 0, 0},
//   {0, 0, 0}
// }

// 선언된 배열에 크기를 지정해서 0으로 초기화 하는 경우
int[][] arr;
arr = new int[2][3]; // 선언 후 초기화하는 경우, new int[][] 생략이 불가능하다.

// 선언과 동시에 값을 넣어 초기화하는 경우
int[][] arr = {{1, 3, 5}, {2, 4, 6}}; // new int[][] 생략이 가능하다.
int[][] arr = new int[][] {{1, 3, 5}, {2, 4, 6}}; 
// 배열은 아래와 같이 초기화가 된다. 
// {
//   {1, 3, 5},
//   {2, 4, 6}
// }

new 키워드와 지정된 크기를 사용해서 배열을 선언하면 먼저 첫번째 지정된 크기만큼 0으로 초기화된 상태의 배열이 만들고 그 내부에 두번째 크기만큼 0으로 초기화된 상태의 배열이 만들어진다. 첫번째 크기로 지정된 배열에는 두번째 크기로 지정된 배열의 참조값을 가지고 있어서 두번째 배열에 접근할 수 있도록 한다.

선언과 동시에 초기화를 했다면 지정된 크기만큼 배열을 만들고 그 내부에 두번째로 지정한 크기만큼 배열을 만들고 각 배열의 공간에 초기값을 넣어준다.

👻가변 배열

가변 배열을 사용하는 방법은?
2차원 이상의 다차원 배열에서는 1차원보다는 자유로운 형태로 배열을 선언하고 공간을 만들 수 있다.
예를 들어, 1부터 100까지의 숫자 조합을 10단위로 끊어서 저장하고 싶다면, 아래와 같이 선언하고 초기화 시킬 수 있다.

ex)
int[][] arr = new int[10][10];
// 배열은 아래와 같이 초기화가 된다. 
// {
//   {0,0,0,0,0,0,0,0,0,0},
//   {0,0,0,0,0,0,0,0,0,0},
//   {0,0,0,0,0,0,0,0,0,0},
//   {0,0,0,0,0,0,0,0,0,0},
//   ...
//   {0,0,0,0,0,0,0,0,0,0}
// }

arr[0][0] = 1;
arr[0][1] = 2;
arr[0][2] = 3;
...
arr[0][9] = 10;
arr[1][0] = 11;
arr[1][1] = 12;
...
arr[9][9] = 100;

// 배열은 아래와 같이 값을 저장한다. 
// {
//   {1,2,3,4,5,6,7,8,9,10},
//   {11,12,13,14,15,16,17,18,19,20},
//   ...
//   {91,92,93,94,95,96,97,98,99,100}
// }

여기서 각 배열에 들어갈 값에 소수는  제외 시키고 넣는다면 불필요한 공간이 남게 된다.
예를 들어 1에서 10사이에는 2,3,5,7 이렇게 4개의 소수가 있고 이것을 제외시키고 넣는다면 10크기의 배열에서 4개의 0으로 초기화된 공간이 남게 되는데, 21에서 30사이에는 단 23, 29 이렇게 단 두 개의 소수만 있기 때문에 여기서는 2개의 0으로 초기화된 공간이 생긴다.
이처럼 모든 배열에 일정하지 않은 공간이 남기 때문에 보통의 방법을 초기화를 하면 메모리를 비효율적으로 사용하는 일이 벌어진다. 이럴 때를 예방하여 처음부터 가변 배열을 사용하면 된다.

가변 배열은 다음과 같이 선언하고 초기화할 수 있다.
int[][] arr = new int[10][];

👻배열과 문자열
문자열 또한 아래와 같이 2차원 배열을 만들어 저장할 수 있다.
ex)
// 가변 배열로 2차원 배열 선언
char[][] letters = new char[3][];
letters[0] = new char[5];
letters[1] = new char[4];
letters[2] = new char[5];

letters[0][0] = 'H';
letters[0][1] = 'e';
letters[0][2] = 'l';
letters[0][3] = 'l';
letters[0][4] = 'o';

letters[1][0] = 'J';
letters[1][1] = 'a';
letters[1][2] = 'v';
letters[1][3] = 'a';

letters[2][0] = 'W';
letters[2][1] = 'o';
letters[2][2] = 'r';
letters[2][3] = 'l';
letters[2][4] = 'd';

하지만 이 방식 역시 너무 비효율적이기 때문에 자바에서는 문자열을 저장하기 위해 Class인 String를 사용한다.
ex)
// 선언 후 각 배열에 값을 넣어 초기화하는 경우
String[] str = new String[3];
str[0] = "Hello";
str[1] = "Java";
str[2] = "World";

// 선언과 동시에 값을 넣어 초기화하는 경우
String[] str = new String[] {"Hello", "Java", "World"};
String[] str = {"Hello", "Java", "World"}; // new String[] 생략 가능

// String Class를 사용해서 선언하고 초기화하면 이렇게 간편하게 만들 수 있다.
String str = "Hello Java World";

👻배열의 길이와 인덱스
배열의 값들을 꺼내 쓰기 위해서는 배열의 인덱스(Index)와 배열의 길이가 필요하다.
인덱스(Index)를 사용하여 특정 값에 접근해 값을 변경하거나 출력할 수 있다.
int[] arr = {1, 2, 3, 4, 5};
System.out.println(arr[2]); // 숫자 3이 출력

arr[2] = 6;
System.out.println(arr[2]); // 숫자 6이 출력

배열의 길이를 구할 때는 length를 사용한다.
int[] arr = {1, 2, 3, 4, 5};
System.out.println(arr.length); // 숫자 5가 출력
그러나 문자열의 길이를 구할 때는 length() 메서드를 사용해야 한다.
String str = "Hello World";
System.out.println(str.length()); // 숫자 11이 출력
2차원 배열의 경우는 길이는 아래와 같이 구할 수 있다.
int[][] arr = {{1, 2, 3}, {4, 5}};
System.out.printfln(arr.length);    // 숫자 2가 출력
System.out.printfln(arr[0].length); // 숫자 3이 출력
System.out.printfln(arr[1].length); // 숫자 2가 출력

배열의 길이는 정수로 최소 0부터 시작할 수 있다.
만약 아래와 같이 배열을 선언한다면, 어떠한 요소도 배열에 넣을 수 없다.
int[] arr = new int[0];

👻반복문을 통한 배열 탐색
int[] arr = {1, 2, 3, 4, 5};

// for문을 사용한 탐색 경우
for(int i = 0; i < arr.length; i++){
   System.out.println(arr[i]);
}
위와 같이 반복문을 통해 배열의 값들을 출력할 수 있다.
그러나 자바에서는 for each문을 통해 좀 더 직관적인 방법으로 배열을 출력할 수 있다.
ex)
int[] arr = {1, 2, 3, 4, 5};

// for each의 구조
for (type var: iterate) {
    body-of-loop
}

// 사용 예시
for (int num: arr) {
   System.out.println(num);
}

for each문은 일반적인 for문처럼 조건식과 증감식을 가지지 않는다.
자동으로 배열을 처음부터 끝까지 순회해주기 때문이다.
하지만 유의해야 할 사항이 있는데, 일반적인 for문은 배열의 각 요소에 접근하여 값을 읽어오고, 값을 재할당하는 것도 가능하지만, for each문은 값을 읽어오는 것만 가능하다.

오늘은 배열, 배열과 반복문의 응용에 대하여 학습했다.
저번 수업 때 너무 어렵게 느껴져 따로 예습을 해왔지만, 역시 직접 코딩을 하려니 막막하기도 하고 너무 어려웠다.
그래도 페어와 함께 머리를 맞대고 고민 끝에 문제를 해결했을 때는 정말 뿌듯하고 짜릿했다. 하지만 오늘은 배열에 대한 지식이 완벽하게 이해된 기분은 아니기에 따로 추가학습을 통해 완벽하게 잡고 갈 생각이다.
오늘도 정말 고생했고, 내일도 파이팅!

profile
개발 공부

0개의 댓글