본문 바로가기

Java

[JAVA / 자바] 배열 (part1)

배열이란?

 

Java에서 배열(Array)은 동일한 데이터 타입의 여러 원소(element)들을 저장하는 자료 구조입니다.

배열은 같은 유형의 변수를 담을 수 있는 고정 크기의 데이터 구조로, 원소는 메모리에 연속적으로 저장됩니다.

 

출처: https://www.ggorantala.dev/introduction-to-array-ds/

배열의 선언

Java에서 배열을 선언할 때에는 데이터 타입을 명시하고, 대괄호를 이용하여 배열임을 나타낸 뒤 배열의 이름을 지정합니다. 예를 들어, int형 배열 numbers를 선언할 때에는 다음과 같이 작성합니다.

int[] numbers;

위 코드는 정수(int)를 원소로 가지는 numbers라는 배열을 선언한 것입니다.

 

배열의 인덱스

Java의 배열은 0부터 시작하는 인덱스(index)를 사용하며, 인덱스를 이용하여 배열의 원소에 접근할 수 있습니다.

배열의 원소들을 초기화하면서 배열을 생성하는 것도 가능합니다.

int[] numbers = {1, 2, 3, 4, 5};

위 코드는 배열 numbers를 선언하고, 원소들을 1, 2, 3, 4, 5로 초기화합니다.

배열의 원소에 접근하려면, 배열 이름 뒤에 대괄호를 이용하여 인덱스를 명시합니다.

int secondElement = numbers[1];

위 코드는 배열 numbers의 두 번째 원소(인덱스 1)를 변수 secondElement에 할당합니다.

 

 

배열의 인덱스 값 변경

Java에서 배열의 인덱스 값은 직접 변경이 가능합니다. 배열 변수명 뒤에 대괄호와 함께 변경하고자 하는 인덱스 값을 지정하고, 대입 연산자와 함께 새로운 값으로 변경하면 됩니다.

다음은 int형 배열에서 인덱스 값을 변경하는 예시 코드입니다.

int[] numbers = {1, 2, 3, 4, 5};  // 배열 변수 선언 및 초기화
System.out.println(numbers[2]);   // 출력: 3

numbers[2] = 10;                  // 인덱스 2의 값을 10으로 변경
System.out.println(numbers[2]);   // 출력: 10

위의 코드에서 numbers 배열 변수의 세 번째 인덱스(2번 인덱스) 값을 출력하고,

이후에 인덱스 2의 값을 10으로 변경하였습니다.

마지막으로 변경된 값이 잘 반영되었는지 출력해 보았습니다.

결과적으로 출력 결과는 각각 3과 10이 됩니다.

 

배열의 인덱스 값을 변경하게 되면 참조하고 있는 배열의 주소값도 변경되는 것일까?

Java에서 배열은 참조 타입이므로, 배열 변수가 참조하는 배열 객체의 주소값을 가지고 있습니다.

배열 변수의 인덱스 값을 변경한다면 해당 인덱스에 저장된 값이 변경되는 것이지, 참조하고 있는 배열 객체의 주소값은 변경되지 않습니다.

즉, 배열 변수의 인덱스 값을 변경한다고 해서 참조하고 있는 배열 객체의 주소값이 변경되는 것은 아닙니다. 배열 변수가 참조하는 배열 객체의 주소값은 배열 변수가 선언될 때 한 번 할당되고, 이후에는 변경되지 않습니다. 이러한 특성은 Java에서 참조 타입에 대한 기본 동작 방식입니다.

 

 

배열의 크기 (arr.length! )

Java의 배열은 크기가 고정되어 있기 때문에, 배열의 크기를 알고 싶을 때는 배열의 length 속성을 사용합니다.

 

int[] numbers = new int[5];
int arraySize = numbers.length;

System.out.println(arraySize); // 배열의 크기: 5

위 코드는 배열 numbers의 크기를 변수 arraySize에 할당합니다

 

Java에서 배열의 크기 정보를 얻는 length 속성은 배열 객체의 내부 필드로 구현되어  있습니다.

따라서, 배열의 길이를 얻을 때에는 메소드 호출 구문인 ()를 사용하지 않고, 배열 객체의 필드에 접근하는 것과 같은 방식 (배열명.length)으로 접근할 수 있습니다.

 

이렇게 생성된 배열 객체는 arr이라는 변수에 할당되며, 객체 내부에는 배열의 크기 정보를 나타내는 length 필드가 포함됩니다. 이 length 필드는 배열 객체가 생성될 때 자동으로 할당되며, 배열의 크기 정보를 저장합니다.

따라서, arr.length는 배열 객체 arr 내부의 length 필드에 접근하여 배열의 크기 정보를 가져옵니다. 

배열의 크기는 변경할 수 없으므로 length 필드는 final로 선언되어 있습니다. length 필드의 값을 변경하려는 시도는 컴파일 오류를 발생시킵니다.

 

 

배열은 메모리에 어떻게 저장될까?

 

Java의 배열은 메모리에 연속적으로 저장됩니다. 배열의 각 원소는 해당 데이터 타입의 크기만큼의 메모리 공간을 차지하며, 인덱스 값에 따라 순서대로 배치됩니다.

예를 들어, int형 원소를 가지는 배열의 경우, 각 원소는 4바이트의 메모리 공간을 차지합니다.

배열의 첫 번째 원소는 인덱스 0에 위치하며, 해당 메모리 주소에서 4바이트만큼의 공간을 차지합니다.

두 번째 원소는 첫 번째 그 다음 메모리 주소에 위치하며, 이후의 원소들도 마찬가지로 순서대로 인접한 메모리 공간에 할당됩니다.

예를 들어, 다음과 같은 int형 배열을 선언하고 초기화한 경우,

 
int[] numbers = {1, 2, 3, 4, 5};

메모리 상에서는 다음과 같이 배열의 원소들이 순서대로 저장됩니다.

위 예시에서, 배열의 첫 번째 원소는 메모리 주소 0x1000에서 4바이트의 공간을 차지하며, 다음 원소들도 순서대로 인접한 메모리 공간에 저장됩니다 . 

Java에서 배열로 선언된 변수와 해당 변수의 인덱스는 메모리의 스택(stack) 영역에 저장됩니다.

스택은 함수 호출 시 사용되는 지역 변수(local variable)들과 함께 사용되며, 변수와 인덱스들도 함수 내의 지역 변수로 취급됩니다.

 

예를 들어, 다음과 같은 int형 배열을 선언하고 초기화한 경우,

int[] numbers = {1, 2, 3, 4, 5};

변수 numbers와 해당 배열의 각 인덱스들은 메모리의 스택 영역에 저장됩니다. 이 때, numbers 변수는 배열의 첫 번째 원소를 가리키는 포인터 역할을 합니다.

위 예시에서, 변수 numbers는 메모리 주소 0x2000에 저장되며, 해당 변수 값으로는 배열의 첫 번째 원소를 가리키는 포인터인 0x1000이 저장됩니다. 이후에는 각 인덱스들의 값이 순서대로 저장되어 있습니다. 인덱스 값은 변수 numbers에 저장된 포인터를 기준으로 오프셋(offset)을 계산하여 참조하게 됩니다.

Java에서 배열 변수는 참조 변수(reference variable)로 취급되며, 해당 변수가 가리키는 실제 데이터는 힙(heap) 영역에 저장됩니다. 배열의 각 원소는 힙 영역에 연속적으로 할당되어 있으며, 해당 주소는 배열의 포인터를 통해 참조됩니다.

 
 

배열 변수와,

변수에 실제로 저장된 데이터가 서로 다른 영역에 저장되는 이유?

 
배열 변수와 해당 변수에 저장된 데이터가 서로 다른 영역에 저장되는 이유는 자바가 객체 지향 언어이기 때문입니다.

배열 변수는 참조 변수(reference variable)로 취급되며, 해당 변수가 가리키는 배열 데이터는 힙(heap) 영역에 저장됩니다. 힙 영역은 객체가 생성될 때 동적으로 할당되며, 객체의 크기와 수명은 프로그램의 실행 도중에 결정됩니다.

배열 데이터가 힙 영역에 저장되는 이유는 배열이 객체이기 때문입니다. 배열은 다른 객체와 마찬가지로 인스턴스(instance)로 생성되며, 생성된 인스턴스는 힙 영역에 할당됩니다. 배열의 각 원소는 힙 영역에서 연속적으로 할당되며, 해당 주소는 배열 변수의 값으로 참조됩니다.

반면에 배열 변수는 메모리의 스택(stack) 영역에 저장됩니다. 스택은 함수 호출 시 사용되는 지역 변수(local variable)들과 함께 사용되며, 변수의 수명은 함수 실행 중에만 유지됩니다. 배열 변수는 해당 배열을 가리키는 포인터 역할을 하며, 해당 배열이 생성된 힙 영역의 주소를 참조합니다.

따라서, 배열 변수와 해당 변수에 저장된 데이터가 서로 다른 영역에 저장되는 이유는 배열이 객체이기 때문입니다. 배열 데이터를 힙 영역에 저장함으로써 객체 지향 프로그래밍에서 필요한 다양한 특성을 구현할 수 있습니다.

 
 

 

배열 데이터를 힙 영역에 저장함으로써, 배열 변수는 참조 변수로 취급되며 배열 데이터는 객체로 취급됩니다.

이는 다형성을 구현하는 데 유용하며, 다양한 객체를 배열로 다룰 수 있게 됩니다.

예를 들어, 여러 종류의 도형 객체를 배열로 다루는 경우, 모든 도형 객체를 배열의 원소로 추가할 수 있습니다. 이 배열은 다형성을 통해 각 도형 객체의 고유한 특성을 유지하면서도 하나의 변수로 다룰 수 있습니다.

또한, 힙 영역에 배열 데이터를 저장함으로써 동적 메모리 할당이 가능해집니다. 배열의 크기를 실행 중에 결정할 수 있으므로, 프로그램의 유연성을 높일 수 있습니다. 또한, 힙 영역은 가비지 컬렉터에 의해 자동으로 관리되므로, 메모리 누수나 불필요한 메모리 사용을 방지할 수 있습니다.

 

참조변수와 연결이 끊어진 배열 데이터는 메모리에서 어떻게 처리가 될까?

Java에서는 Garbage Collector가 더 이상 사용되지 않는 메모리를 자동으로 수거하여 반환합니다.

따라서 아래와 같이 재할당 등으로 연결이 끊어진 배열은 더 이상 참조하는 변수가 없을 때, Garbage Collector에 의해 메모리에서 자동으로 제거됩니다.

String[] sArr = {"가", "나", "다"};

sArr = new String[5];

 

 

 

얕은복사 vs 깊은복사 관련: 다음 포스팅에..

 

https://mundol-colynn.tistory.com/131

 

[ 자바 / Java ] 얕은복사 vs 깊은복사 (shallow copy /deep copy) 배열 (part2)

https://mundol-colynn.tistory.com/127 [JAVA / 자바] 배열1 배열이란? Java에서 배열(Array)은 동일한 데이터 타입의 여러 원소(element)들을 저장하는 자료 구조입니다. 배열은 같은 유형의 변수를 담을 수 있는

mundol-colynn.tistory.com

 

Reference

  1. https://docs.oracle.com/javase/tutorial/java/nutsandbolts/arrays.html
  2. https://stackoverflow.com/questions/11007496/change-a-value-in-an-array-using-java