고래씌

[JAVA] 5. 배열 본문

JAVA/JAVA 기초

[JAVA] 5. 배열

고래씌 2023. 10. 10. 14:32

1. 배열

1) 변수 : 하나의 공간에 하나의 값을 담을 수 있음
               ex) int a = 10;
                          a = 20;
 
2) 배열 : 하나의 공간에 여러개의 값을 담을 수가 있음(단, 같은 자료형의 값만)
              정확히 얘기하면 배열의 각 인덱스 위치에 실제 값이 담긴다.(인덱스는 0부터 시작)
              ex) int [] a = new int[3];   // int 자료형의 값만 담을 수 있는 3개의 공간이 할당된다.
                                                     // 방 [0], [1], [2] 방 3개를 만듦
                                                     // 인덱스는 항상 0부터 시작한다
                                                     // 배열의 크기는 3, 마지막 인덱스 = 3-1 == 2
                                                     // 배열의 크기 만약 100이라면 마지막 인덱스는 99
                                                     // 크기 = n , 마지막 인덱스 == n-1
 
 
Q. 배열을 왜 써야할까?
- ex) 만약 변수만을 가지고 프로그래밍을 하게 된다면...
- 0, 1, 2, 3, 4 기록해야됨.
 

(1)변수를 이용하여 각 데이터를 보관하고자 할 때 -> 각각의 변수를 만들어서 따로 관리해야한다.

int num1 = 0;
int num2 = 1;
int num3 = 2;
....
저장된 값을 전부다 출력해야한다면?  => 반본문 사용하지 못함
for(int i=1; i<=5; i++) {                        (X)
    System.out.println(num+i);
 

(2) 배열을 가지고 프로그래밍시

0, 1, 2, 3, 4
1) 배열 선언
0,1,2,3,4
 

3) 배열 선언

자료형 [] 배열명;
자료형 배열명 [];
int a;     // 변수
int [] arr;     // 배열
int arr[];    // 배열
 

4) 배열 할당

- 이 배열에 몇 개의 값들을 보관할건지 배열의 크기를 지정해주는 과정
- 지정한 갯수만큼 값이 들어간 공간이 확보됨
 
[표현법]
배열명 = new 자료형[배열의 크기];
ex) arr = new int[5];
 
int [] arr = new int [5];
System.out.println(arr);    // 이렇게 치면 메모리 값이 출력됨
// [I@53bd815b  출력됨.     => 여기서 @ 기준으로 우측은 참조하고 있는 메모리 영역의 주소값
// [ : 배열을 의미함
// I : int자료형임을 의미
 
 

5) 배열의 각 인덱스(방번호) 자리에 값 대입

[표현법]
배열명[방번호(==인덱스)] = 값;
arr[0] = 0;
arr[1] = 1;
arr[2] = 2;
 ...

public void method1() {

    int [] arr = new int [5];
    System.out.println(arr);    // 이렇게 치면 메로리 값이 출력됨
    // [I@53bd815b 여기서 @ 기준으로 우측은 참조하고 있는 메모리 영역의 주소값
    // [ : 배열을 의미함
    // I : int자료형임을 의미

    for(int i=0; i<5; i++) {
        arr[i] = i;  // arr[0] = 0; 다음 반복시 arr[1] = 1;
    }

//		arr[0] = 0;
//		arr[1] = 1;
//		arr[2] = 2;

    // 반복문을 활용한 값대입 / 출력도 가능함
    for(int i=0; i<5; i++) {
        // 각각의 인덱스 값을 확인하기 위해서는 []블럭을 통해 내가 접근하고자하는 인덱스를 제시해줘야 한다
        System.out.printf("%d번의 인덱스 값 : %d \n", i, arr[i]);
    }
}

 

2. 배열 메모리구조

메소드 내부에서는 무조건 Stack 안에 들어감

 

int [] => 참조형
배열 선언시 Stack 안으로 들어감.
 

new라고 선언한 배열은 무조건 Heap 영역안에 들어감
Heap 영역 안에 저장공간을 하나 만듦.

Heap 메모리 영역은 절대!!!!!!!!!!! 값이 빈공간이 없어야한다!!! -> JVM이 최초 할당 시 기본값을 자동 저장해줌
각 배열의 인덱스에는 기본값인 0이 들어가 있는 상태이다. (int 자료형)
 
☞ 정리
int [] arr = new int[5];  // 배열
① arr 이라는 배열명을 가진 int형 배열 박스가 stack영역에 만들어짐
② 그 다음 new int[5]가 실행된다.실행시 크기 5짜리 저장공간이 heap 영역에 생성됨
(new라는 키워드가 붙은 모든 코드는 항상 heap 영역에 값이 할당됨)
③ 5칸짜리 저장공간이 만들어진 후에 각 인덱스에는 자료형에 맞는 기본값으로 데이터가 초기화 되고, 그 후 주소값이 할당됨(0x123)
④ 그 다음 주소값을 대입연산자를 통해 arr 배열에 할당한다.
 
- 기본자료형(boolean, char, byte, short, int, long, float, double)
=> 실제 값(리터럴)을 바로 stack 영역에 담을 수 있는 변수 => 일반변수

- 그 외 자료형(int[], double[], String, Scanner, ...)
→ heap 영역안에 생성된 데이터의 주소값을 담고있는 변수
→ 주소값을 참조한다(참조변수) => 레퍼런스 변수
 
* hasCode() 함수 사용

public void method2() {

    int i = 10; // 일반 변수

    int [] arr = new int[5];  // 배열

    System.out.println("i : " + i);  // 10
    System.out.println("arr : " + arr); // [I@16진수랜덤값
    System.out.println("arr의 해시코드값 : "+arr.hashCode());   // 16진수값만 빼서 10진수로 변환을 해줌
    // A == B A와 B의 주소값이 일치하냐? 볼때 hashCode()를 사용하여 동등비교 가능
    // 해시코드() : 주소값을 10진수의 형태로 변환한 것

    double [] dArr = new double[3];
    System.out.println(dArr);
    System.out.println("dArr의 해시코드값 : "+dArr.hashCode());
}


3. 배열 length

- 문자열의 크기를 아는 방법 : 문자열.length();
- 배열의 크기를 아는 방법 : 배열명.length;

public void method3() {
    int[] iArr = new int[3];

    double[] dArr = new double[3];

    for(int i=0; i<3; i++) {    // 0, 1, 2
        System.out.println(iArr[i]);
    }

    // 문자열의 크기를 아는 방법 : 문자열.length();
    // 배열의 크기를 아는 방법 : 배열명.length;
    for(int i=0; i < dArr.length; i++) {   // 0, 1, 2
        System.out.println(dArr[i]);
    }
}

ArrayIndexOutOfBoundException
=> 배열의 인덱스 범위를 벗어남
 

4. 배열 자료형 에러들

1. ArrayIndexOutOfBoundException
=> 배열의 인덱스 범위를 벗어남
 
2. NullPointerException
=> 기본값이 null인 변수를 가지고 "메소드를 호출" 한다던가 혹은 특정 "필드"에 접근하려고 할 때 발생하는 에러.
  null값을 가지고 있다고 해서 반드시 에러가 발생하는게 아니다.
  null값을 통해 어딘가를 참조하려고 할 때 에러발생!!!
  ex) arr.length();, arr[0], arr.length;

public void method5() {
    int i = 0;
    String str = null;    // 참조자료형의 기본값은 null

    // int i2 = null; null 값은 참조자료형에만 대입 가능
    int [] arr = null; // null값 할당 가능. 주소값을 담을 수 있는 변수들의 기본값은 null;

    System.out.println(arr.hashCode());
}
public void method6() {
    int [] arr = new int[5];
    /*
     * arr[0] = 2;
     * arr[1] = 4;
     * arr[2] = 6;
     * arr[3] = 8;
     * arr[4] = 10;
     */


    for(int i=0; i<arr.length; i++) {
        arr[i] = 2*(i+1);
        System.out.printf("arr[%d] = %d\n", i, arr[i]);
    }
}


5. 배열의 크기 변경

1) 한번 지정한 크기는 변경이 불가능함.
=> 배열의 크기를 변경하고자 한다면 새로운 배열을 선언해야함
항상 고유한 주소값 부여, 기존에 생성되었던 주소와는 절대로 겹치지 않는다.
기존에 참조하고 있던 연결이 끊기고 새로운 곳 참조
 
연결이 끊어진 기준의 배열은 Heap 영역 내부에 떠다님
일정시간이 지나고서도 사용이 되지 않으면 가비지 컬렉터가 수거해간다. (자동 메모리 관리)
=> 자바에서 자동메모리관리 특징 => 개발자가 코드에만 신경쓸 수 있게 해준다.

public void method6() {
    int [] arr = new int[5];

    System.out.println("==== arr 변경전 ====");
    System.out.println(arr);
    System.out.println("arr의 해시코드값 : "+arr.hashCode());

    arr = new int[7];    // 새롭게 배열을 선언하고 할당해야함

    System.out.println("==== arr 변경후 ====");
    System.out.println(arr);
    System.out.println("arr의 해시코드값 : "+arr.hashCode());
    System.out.println(arr[0]);  // 0
}

6. 배열 선언과 동시에 할당(크기 지정)

int [] arr = new int[4];   // 배열변수 선언 및 할당 및 초기화

☞  각 인덱스에 값들을 직접 대입하여 초기화
arr[0] = 1;
arr[1] = 2;

☞ 배열 선언과 동시에 초기화가지 한번에 끝내는 방법
- 방법 1.
▷ int[] arr1 = new int[] {1, 2, 3, 4};
 
- 방법 2.
▷ int[] arr2 = {1, 2, 3, 4};   // new int[] 생략
 
▷ arr1 == arr2 
- arr1 -> arr1의 heap영역의 주소값
- arr2 -> arr2의 heap영역의 주소값
=> 주소값 == 주소값 : 주소값간의 동등비교를 수행하기 때문에 false가 나옴
 
▷ int[] arr3 = arr2;
▷ arr2 == arr3;  
=> 얕은 복사를 활용하여 메모리 주소값만 복사를 한다. 그렇기 때문에 true가 나옴

public void method7() {
    // 배열 선언과 동시에 할당(크기 지정)
    int [] arr = new int[4];   // 배열변수 선언 및 할당 및 초기화

    // 각 인덱스에 값들을 직접 대입하여 초기화
    arr[0] = 1;
    arr[1] = 2;

    // 배열 선언과 동시에 초기화가지 한번에 끝내는 방법
    // 방법 1.
    int[] arr1 = new int[] {1, 2, 3, 4};

    // 방법 2.
    int[] arr2 = {1, 2, 3, 4};   // new int[] 생략

    int[] arr3 = arr2;   // 얕은 복사 (메모리 주소값만 복사)

    System.out.println(arr1 == arr2);
    // arr1 -> arr1의 heap영역의 주소값
    // arr2 -> arr2의 heap영역의 주소값
    // 주소값 == 주소값 : 주소값간의 동등비교를 수행하기 때문에 false가 나옴

    System.out.println(arr2 == arr3);    // true   얕은복사
    System.out.println(arr2[2]);
    arr3[2] =999;   
    System.out.println(arr2[2]);   

}
public void method8() {
    /*
     * 1. 크기 10짜리 정수배열 선언
     * 
     * 2. 반복문을 활용해서 0번 인덱스부터 마지막 인덱스까지 순차적으로 접근하면서 값을 대입.
     *    대입할 값은 1~100까지 범위의 랜덤값
     *  
     * 3. 반복문을 활용해서 0번 인덱스부터 ~ 마지막 인덱스까지 배열에 담긴 값을 출력
     * 출력예시) arr[x] : xx 
     */


    int[] arr = new int[10];

    for(int i=0; i<arr.length; i++) {
        arr[i] += (int) (Math.random()*100+1);
        System.out.printf("arr[%d] : %d\n", i, arr[i]);
    }
}