자바 기본 배우기 -배열의 기초-

1. 배열은 왜 필요한가?

 

어떤 집단의 학생 이름을 String 타입으로 다음과 같이 저장했다

 

String name1 = "태연"

 

String name2 = "아이유"

 

String name3 = "수지"

 

String name4 = "윤아"

 

String name5 = "신세경"

 

String name6 = "카리나"

 

근데 이 집단에 학생이 더 들어온다면.. name7,name8,.... 계속 이렇게 저장해야하나?

 

변수의 수도 계속 증가하고, 코드 길이도 계속 증가하게 될것

 

이름이 name1,name2,... 비슷해보여도 전혀 다르기 때문에 반복문으로 확인하기도 어렵고

 

갑자기 어느날에는 학생이 10명이었다가, 다음날에는 학생이 100명이 었다가.. 다음날에는 학생이 50명이었다가..

 

동적으로 변한다면? 그때마다 손으로 써야하나?

 

 

2. 배열이란..

 

1) "같은 종류, 같은 타입"의 데이터를 저장하기 위한 자료구조

 

----------------------------------------------------------------------------------

 

***여기서 주의할 점은 "같은 종류"

 

파이썬의 리스트는 다른 종류도 저장할 수 있잖아... 그래서 배열이라고 부르기 어렵다...

 

----------------------------------------------------------------------------------

 

2) 그리고 크기가 고정되어 있다

 

즉, 한번 생성된 배열은 크기를 바꿀 수 없다

 

5칸짜리 배열 arr을 생성했는데 갑자기 2배로 늘리고 싶다면?

 

10칸짜리 새로운 배열 만드는 방법 말고는 없다

 

근데 ArrayList라는 객체가 있긴하다.. 적당히 크기가 차면 내부적으로 알아서 크기를 키우고 가지고 있던 원소를 알아서 복사해놓음

 

3) 배열은 객체로 취급

 

4) 배열의 요소를 참조하려면 배열의 이름과 index라고 하는 int 유형의 정수 값을 조합해서 사용

 

배열도 참조형으로 주소를 가지고 있어

 

--------------------------------------------------------------------------------------------------------------------------

 

3. 참조형은 무슨 특징을 가지나?

 

JVM 메모리 공간은 크게 나누면 stack영역과 heap 영역으로 나뉨

 

지역변수, 메인 메소드는 stack 영역에 생성

 

참조형, 객체 등등은 heap에 생성

 

 

 

 

 

기본 자료형 int는 stack에 공간을 생성하고 값을 직접 저장하지만.. 참조형 String은 stack에 주소를 저장하고

 

주소는 heap에 저장되어 그 주소 안에 값을 저장함

 

String Campus1 = '서울'하면.. Campus1이라는 공간이 stack에 생기고 어떤 주소값을 저장

 

그 주소값은 heap이라는 공간에 생성되고 그 안에 "서울"을 저장

 

 

 

new라는 명령어로 생성한 객체가 heap에 생성

 

일반적인 참조형 객체는 new라는 명령어로 생성하는데 자주 사용되는 특별한 참조형들은 안쓰고도 생성가능함

 

대표적으로 String은 다음과 같이 생성가능

 

1) String campus1 = "서울"

 

2) String campus1 = new String("서울")

 

둘의 차이가 어디서 나오냐면...

 

package java03.operator;

public class Op03 {
    public static void main(String[] args){
        String c = "Hi";
        String d = "Hi";
        String e = new String("Hi");
        
        System.out.println(c == d);
        System.out.println(c == e);
        System.out.println(c.equals(e));
    }
}

 

위 코드를 수행하면 true, false, true가 나오는데 각 변수가 가리키는 형태가 다음과 같다

 

 

 

 

그래서 c == d하면 c와 d가 주소가 동일해서 true가 나오고.. c == e하면 c와 e는 주소가 다르니 false가 나옴

 

하지만 사람이 원하는건 주소가 동일한지 여부가 아니라 안에 든 내용물이 동일한지를 알고 싶은거다

 

내용이 동일한지 검사하는 메소드 equals를 이용하면.. c.equals(e)하면 true가 나온다

 

그래서 기본적으로 내용이 동일한지 검사하고 싶다면 equals()를 쓰는게 좋다

 

--------------------------------------------------------------------------------------------------------------------------

 

그러면 배열도 참조형이라는데 어떻게 저장이 되느냐

 

5칸의 크기를 가지는 score라는 배열 객체 생성한다면... stack에 score 공간이 생기고... heap에 5칸의 크기를 가지는 공간이 생성

 

그리고 heap에 배열이 시작되는 주소값을 score가 저장

 

 

 

만약 int배열이라면.. 각 크기가 4byte씩 가지는데 0번 인덱스는 시작 주소에서 바로 접근 가능하고

 

1번 인덱스는 0번 인덱스에서 4byte 옆으로 이동하면 있으니까 접근 가능하고...

 

...

 

그래서 각 영역에 접근할려면 이제 score[0], score[1], ...으로 접근

 

아무튼 배열도 객체로 취급하며 new라는 명령어로 생성해야하고

 

그러한 new라는 명령어를 쓰는 순간 heap 영역에 생성이 된다

 

score라는 배열을 그냥 출력해보면 안에 가지고 있는 주소를 출력하게 되어서 읽을 수없는 이상한 값들이 나옴

 

 

campus라는 배열 생성하면 위 그림과 같이 stack에 campus라는 공간 생성되고 heap에 연속된 5개의 크기를 가지는 공간이 생성된다

 

campus에는 시작 주소가 저장되어 있고.. heap에 각 영역 0,1,2,3,4에도 주소가 있네 왜??

 

만약에 int형 배열이라면.. 다음과 같이 heap의 각 공간에는 정수값이 그대로 있을 거임

 

score = [60,90,70,20,50]이라면... heap의 각 공간에는 주소가 아니라 60,90,70,20,50이 각각 들어있음

 

int는 기본 자료형이라 그렇다

 

 

 

하지만 string 배열이라면.. campus = ['a','b','c','d','e'] 이러면..?

 

String은 참조형이라 각 공간에는 각 string을 참조할 주소를 가지고 있다

 

 

 

4. 배열의 선언

 

2가지 방법으로 선언 가능

 

1) 타입[] 이름 : int[] arr

 

2) 타입 이름[] : int arr[]

 

주로 첫번째 방법을 사용함

 

 

5. 배열의 생성과 초기화

 

 

1) 선언과 동시에 초기화

 

타입[] 이름 = {값1, 값2, 값3, 값4,....};

 

 

2) 생성 및 값 초기화

 

이름 = new 타입[] {값1, 값2, 값3, 값4,....};

 

위 2가지는 내가 직접 값을 일일이 넣고 싶은 경우에 사용

 

 

3) 자료형의 초기값으로 초기화하여 생성

 

이름 = new 자료형[길이];

 

길이를 반드시 입력해야함

 

이런 경우에는 해당 길이만큼 배열 생성 후에 자바에서 알아서 해당 자료형의 초기값을 집어넣는다

 

char 같은 경우는 '\u0000' 이게 공백문자를 나타내는 유니코드

 

 

 

그러면 strarr = new String[10]은 초기값을 뭘로 넣을까?

 

String은 참조형이니까 null로 넣는다

 

그러면 datearr = new Date[10]은??? 역시 Date는 참조형이니까 null로 넣는다

 

기초 자료형 int, short, byte, boolean, char, long, float, double 8가지 제외한 나머지는 모두 참조형

 

 

6. 배열의 사용

 

1) index 번호를 가지고 각 요소에 접근 가능

 

주의할 점은 index 번호는 0부터 시작한다

 

2) (배열이름).length를 이용해 배열의 길이 조회 가능하다

 

배열의 길이가 arr.length라고 해도 index는 0번부터 시작하니까 0번부터 arr.length-1까지만 접근 가능하다

 

//0번부터 arr.length-1까지 조회

for(int i = 0; i < arr.length; i++){
    arr[i]
}

 

3) 배열의 길이는 임의로 변경 불가능하다

 

길이를 바꾸고 싶다면, 새로운 배열을 생성하고 기존 배열의 원소를 옮겨줘야한다

 

arr = new int[3]으로 배열 하나 생성

 

 

다음 temp = new int[5]라는 배열을 또 하나 생성

 

 

 

반복문 같은 것으로 arr의 내용을 temp로 잘 복사했다고 한다면..

 

 

 

복사하고 나서 arr = temp로 대입

 

arr의 참조가 바뀔 것이다

 

 

그러면 temp도 temp = null 같은 것으로 대입해서 연결을 끊어야하나?

 

해도 되고 안해도 되고.. 큰 영향 없다

 

아무튼 원래 arr이 가리킨 크기 3인 배열은 어떻게 되나?

 

자바에서도 파이썬처럼 메모리 관리를 알아서 해줌

 

JVM 내에서 Garbage Collector라고 불리는 GC가 더 이상 쓰지 않는 크기 3인 원래 배열을 어느 시점에서 알아서 처리함

 

 

 

GC는 강제로 동작시킬수도 있지만 절대 추천하지 않는다

 

 

7. 배열의 메모리 생성 과정

 

 

 

int[] points 순간에 stack에 points라는 공간 생기고 null을 저장

 

new int[3]을 수행하는 순간 heap에 int의 초기값 0으로 초기화된 3개 연속된 공간의 배열 생성

 

시작 주소는 0x100으로 되어있는데 예시일 뿐

 

그리고 points = new int[3]으로 할당 연산을 수행하면...

 

stack의 points가 시작 주소 0x100을 저장

 

그래서 배열의 시작 주소를 담아서 points가 참조하게 된다

 

그 후 points[0] = 1; points[1] = 'A';로 각 공간에 값 할당

 

여기서 'A'는 아스키코드로 65라고 함

 

int는 4byte인데 char은 2byte라서 강제 형변환이 일어나고 에러 없이 65로 저장

 

package java05.array;

public class Array01_생성 {
	public static void main(String[] args) {
		//1차원 배열을 선언하는 방법
		int[] score1; //주로 사용
		int score2[];
		
		//score1 = {1,2,3,4,5}; //이거는 불가능함
		//생성과 함께 값을 직접 넣어야만 가능
		
		score1 = new int[] {1,2,3,4,5};
		
		int [] score3 = {1,2,3,4,5};
		
		//길이를 이용한 생성
		int[] score4 = new int[5];
		score4[0] = 10;
		score4[1] = 20;
		score4[2] = 30;
		score4[3] = 40;
		
		for(int i = 0; i < score4.length; i++) {
			System.out.println(score4[i]);
		}
	}
}

10
20
30
40
0

 

나는 마지막에 0을 넣은 적이 없는데 왜 0이 콘솔에 출력되었나?

 

int[] score4 = new int[5]; 하면서 자바가 알아서 int의 초기값 0을 크기 5인 배열에 넣어놨고

 

내가 직접 넣은건 0,1,2,3번 위치에 10,20,30,40을 넣었으니

 

마지막 4번 위치에는 여전히 0이 있다

 

 

8. for-each

 

가독성이 개선된 반복문

 

배열, Collections에서 사용

 

index 대신 직접 원소에 접근하는 변수를 제공해줌

 

오직 읽기만 가능하며 원본 값을 변경할 수는 없다(naturally read only)

 

package java05.array;

public class Array02_foreach {
	public static void main(String[] args) {
		
		int[] arr = new int[6];
		
		arr[0] = 77;
		arr[1] = 50;
		arr[2] = 10;
		arr[3] = 12;
		arr[4] = 64;
		arr[5] = 15;
		
		for(int x: arr) {
			System.out.println(x);
		}
	}
}

77
50
10
12
64
15

 

arr에 들어가서 값을 복사해서 x에 저장해서 x를 한줄에 하나씩 출력

 

복사해와서 x값은 변경 가능함

 

//복사해와서 저장한 변수 x는 변경 가능함
for(int x: arr) {
    x *= 2;
    System.out.println(x);
}

154
100
20
24
128
30

 

하지만 원본 arr 내부 값들은 변경 불가능함

 

//원본 내부 값들은 변경 불가능하다

for(int x: arr) {
    x *= 2;
}

for(int x: arr) {
    System.out.println(x);
}

77
50
10
12
64
15

 

원본 값들을 변경하고 싶다면? index로 직접 접근해서 바꿔줘야함

 

//원본 값들을 바꾸고 싶다면 index로 직접 접근해서 변경

for(int i = 0; i < arr.length; i++) {
    arr[i] *= 2;
}

for(int x: arr) {
    System.out.println(x);
}

154
100
20
24
128
30

 

 

9. 파이썬의 리스트처럼 출력하기

 

단순히 arr을 출력하면 주소가 출력되는데

 

Arrays.toString(arr)을 출력하면 파이썬의 리스트처럼 출력됨

 

물론 반복문으로 하나씩 출력할수도 있긴함

 

System.out.println(arr); //주소 출력
System.out.println(Arrays.toString(arr));

[I@2a139a55
[77, 50, 10, 12, 64, 15]

 

10. 배열의 복사

 

배열은 생성하면 길이를 변경할 수 없다

 

더 많은 저장공간이 갑자기 필요하다면, 큰 배열을 생성하고 이전 배열의 값을 복사해야한다

 

System.arraycopy라는게 있나봐

 

 System.arraycopy(원본 배열, 복사의 시작 위치(0번부터 아니여도 됨), 복사해서 넣을 배열, 복사해서 넣을 배열의 어디 위치부터 넣을지, 복사할 크기)

 

 

 

복사할 크기는 원본 배열에서 얼마나 복사해올것인가

 

시작위치가 0번이고 복사할 크기가 arr.length라면 0~arr.length-1까지 복사하면 총 길이가 arr.length만큼이겠지

 

package java05.array;

import java.util.Arrays;

public class Array03_copy {
	public static void main(String[] args) {
		int [] arr = {77, 50, 10, 12, 64, 15};
		
		int[] temp = new int[arr.length*2];
		
		//반복문으로 복사하기
		
		for(int i=0; i<arr.length; i++) {
			temp[i] = arr[i];
		}
		
		System.out.println(Arrays.toString(temp));
		
		int[] temp2 = new int[arr.length*2];
		//System.arraycopy로 복사
		
		//arr의 내용을 0번부터 arr.length 길이만큼(arr.length-1까지)
		//복사해서 temp2에 0번부터 집어넣는다
		System.arraycopy(arr,0,temp2,0,arr.length);
		
		System.out.println(Arrays.toString(temp2));
		
	}
}

[77, 50, 10, 12, 64, 15, 0, 0, 0, 0, 0, 0]
[77, 50, 10, 12, 64, 15, 0, 0, 0, 0, 0, 0]

 

11. 연습문제

 

1) 배열에서 최댓값, 최솟값을 찾기

 

package java05.array;

public class Array04_problem {
	
	public static void main(String[] args) {
		int[] intArray = {3,27,13,8,235,7,22,9,435,31,54};
		
		//최댓값과 최솟값 찾기
		int max_value = 0;
		int min_value = 10000;
		
		for(int x: intArray) {
			if(x > max_value) {
				max_value = x;
			};
			
			if(x < min_value) {
				min_value = x;
			};
			
		}
		
		System.out.println(max_value);
		System.out.println(min_value);
	}

}

435
3

 

 

혹은 메소드를 이용해서 다음과 같이 풀 수 있나보다

 

//여러 메소드 이용

int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;

for(int num: intArray) {
    min = Math.min(min, num);
    max = Math.max(max, num);
}

System.out.printf("min: %d, max: %d\n", min,max);

min: 3, max: 435

 

2) 원소의 빈도수 구하기

 

제시된 배열 내에 존재하는 원소들의 count를 세서 대응하는 index에 저장

 

package java05.array;

import java.util.Arrays;

public class Array05_problem2 {
	public static void main(String[] args) {
		
		int [] intArray = {3,7,2,5,7,7,9,2,8,1,1,5,3};
		//각 원소의 빈도수를 세서 배열의 대응하는 index에 저장
		
		int [] count = new int[10];
		
		for(int x: intArray) {
			count[x]++;
		}
		
		System.out.println(Arrays.toString(count));
	}
}

[0, 2, 2, 2, 0, 2, 0, 3, 1, 1]
TAGS.

Comments