같은 타입의 변수 여러 개를 묶은 것.
배열을 변수에 담으면, 그 변수는 참조 변수가 되어 실제 배열이 담겨있는 메모리의 주소만을 가리킬 뿐, 배열(데이터) 자체를 직접 저장하지는 않음.
배열의 선언은 원소의 타입과 변수 이름으로 할 수 있음.
원소의 타입[] 변수 이름;
int
형인 원소를 담는 배열 arr
를 선언하려면,
int[] arr; // 배열을 다루기 위한 참조 변수 선언.
배열을 실제로 생성하려면 new
연산자와 함께 배열의 타입과 길이를 정해주어야함.
int[] arr;
arr = new int[10]; // int 타입의 원소를 가진 길이 10의 배열 생성.
이 때, 배열의 원소는 int
형의 기본값인 0으로 초기화 되어있는 상태
자료형 | 기본값 |
---|---|
boolean | false |
char | '\u0000' |
byte , short , int | 0 |
long | 0L |
float | 0.0f |
double | 0.0d 또는 0.0 |
참조형 변수 | null |
배열의 원소에 0부터 순서대로 부여된 고유한 숫자
배열 이름[인덱스]
로 해당 인덱스의 원소에 접근 가능.
arr[3] = 48;
인덱스에는 상수, 변수, 수식 사용 가능.
int x = 1;
arr[x + 3] = 77;
📌 [주의]
배열의 인덱스로 변수를 사용하는 경우, 유효 범위 밖의 인덱스를 사용하지 않도록 주의해야 함.
변수는 런타임에 실제 값으로 대입되므로, 그 전에 컴파일 과정에서는 컴파일러가 이런 실수를 잡아주지 못함.
그대로 실행 단계까지 가면
ArrayIndexOutOfBoundsException
에러 발생.
배열을 생성할 때, 길이는 반드시 0 또는 int
형인 양의 정수여야 함.
배열은 한 번 생성되면 길이 변경이 불가능하므로 길이는 상수임.
모든 배열이 갖고 있는 length
속성을 통해 배열의 길이를 얻을 수 있음.
보통 for
문을 돌릴 때 조건식으로 활용함.
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]); // 배열의 모든 원소를 한번씩 거치며 그 원소를 출력
}
배열의 길이를 변경하려면 더 큰 길이의 새 배열을 만들고 기존 원소들을 그쪽으로 복사해야 함. (애초에 넉넉하게 생성하는 것이 좋음)
배열은 생성과 동시에, 각 원소에 자신의 타입에 맞는 기본값이 할당됨.
만약 원하는 값을 넣으려면 일일이 지정해줘야함.
int[] numbers = new int[5];
numbers[0] = 1;
numbers[1] = 2;
numbers[2] = 3;
numbers[3] = 4;
numbers[4] = 5;
배열의 크기가 커질수록 비효율적인 방식이기 때문에 다음과 같은 방식으로 초기화를 할 수 있음.
int[] numbers = new int[] {1, 2, 3, 4, 5}; // 길이는 명시하지 않아도 뒤에 오는 원소의 개수를 세어서 자동으로 length에 담아줌
// 또는
int[] numbers = {1, 2, 3, 4, 5}; // new int[] 생략 가능
중괄호{}
안에 아무 것도 넣지 않으면 길이가 0인 빈 배열이 생성됨.
int[] numbers = new int[] {};
// 또는
int[] numbers = {};
배열을 담고 있는 참조변수를 출력하면 "타입@주소" 형식의 결과가 나온다.
System.out.println(numbers); // 출력 값: [I@4617c264
@을 기준으로 앞은 1차원 int
배열이라는 의미, 뒤는 배열의 내부 주소(16진수)
배열을 출력하는 방법은 일반적으로 2가지 있는데,
하나는 for
문을 돌리는 방식 (위에서 이미 사용했으므로 생략),
다른 하나는 Arrays.toString()
메서드를 활용하는 방식임.
System.out.println(Arrays.toString(numbers)); // 출력 값: [1, 2, 3, 4, 5]
출력 값의 생김새가 배열과 똑같지만 문자열임
[참고] println()
메서드는 char
배열을 출력하면 구분자 없이 그대로 출력하도록 설계됨.
char[] charArr = {'a', 'b', 'c'};
System.out.println(charArr); // 출력 값: abc
int[] arr = {1, 2, 3, 4, 5};
int[] newArr = new int[arr.length * 2]; // 기존 배열의 길이의 2배 정도가 적절
for (int i = 0; i < arr.length; i++) {
newArr[i] = arr[i]; // arr의 각 원소의 값을 newArr 내의 동일 인덱스에 할당
}
arr = newArr; // 참조변수 arr가 새로운 배열인 newArr을 가리키게 됨
자신을 가리키는 참조변수가 사라진 기존 배열 {1, 2, 3, 4, 5}는 GC에 의해 자동적으로 메모리에서 제거됨.
파라미터로 복사할 배열의 이름, 복사 시작 인덱스, 붙여넣을 배열의 이름, 붙여넣기 시작 인덱스, 복사할 원소의 개수가 들어감.
System.arraycopy(arr, 0, newArr, 0, arr.length);
배열 arr
의 0번째 인덱스부터 arr.length
개 만큼 복사한 뒤, newArr
의 0번째 인덱스부터 붙여넣겠다는 의미.
이 때 인덱스 설정을 잘못하면
ArrayIndexOutOfBoundsException
에러 발생
교재에 제시된 예제로 실습
class ArrayExample1 {
public static void main(String[] args) {
int sum = 0; // 총합을 0으로 초기화
float avg = 0; // 평균을 0으로 초기화
int[] scoreArr = {65, 77, 100, 94, 82};
for (int i = 0; i < scoreArr.length; i++) {
sum += scores[i];
}
avg = sum / (float)scoreArr.length; // 우변의 연산결과를 float형으로 만들기 위한 형변환
System.out.printf("총합: %d%n평균: %2.1f", sum, avg); // 평균(avg)는 소수점 밑 1자리까지만 표현
}
}
class ArrayExample2 {
public static void main(String[] args) {
int[] scoreArr = {65, 77, 100, 94, 82};
int maxScore = scoreArr[0]; // 최대값을 첫번째 원소 값으로 초기화
int minScore = scoreArr[0]; // 최소값을 첫번째 원소 값으로 초기화
for (int i = 0; i < scoreArr.length; i++) {
if (scoreArr[i] > maxScore) {
maxScore = scoreArr[i]; // 지금 선택된 원소가 최대값보다 크면 이 원소를 최대값으로 지정함
}
if (scoreArr[i] < minScore) {
minScore = scoreArr[i]; // 지금 선택된 원소가 최소값보다 작으면 이 원소를 최소값으로 지정함
}
}
System.out.println(maxScore);
System.out.println(minScore);
}
}
class ArrayExample3 {
public static void main(String[] args) {
int[] arr = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
for (int i = 0; i < arr.length; i++) {
int randomNum = (int)(Math.random() * 10); // 0~9 사이의 랜덤 int 생성
int tmp = arr[i];
arr[i] = arr[randomNum];
arr[randomNum] = tmp;
}
System.out.println(Arrays.toString(arr)); // [6, 4, 9, 2, 8, 0, 3, 7, 5, 1] (매번 랜덤하게 원소가 섞인 배열 출력)
}
}
public class ArrExample4 {
public static void main(String[] args) {
int[] arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int)(Math.random() * 10);
}
System.out.println("기존 배열: " + Arrays.toString(arr));
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < (arr.length - 1); j++) {
if (arr[j] < arr[j + 1]) {
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
System.out.println("내림 차순으로 정렬: " + Arrays.toString(arr));
}
}
⚡️ [참고] 버블 정렬(bubble sort) 알고리즘
인접한 2개의 원소를 비교하여 크기가 순서대로 되어있지 않으면 서로 자리를 바꾸는 방식.
단순하지만, 시간복잡도 측면에서 비효율적. O(n2)
public class ArrExample5 {
public static void main(String[] args) {
int[] numArr = new int[10];
int[] counter = new int[numArr.length];
for (int i = 0; i < numArr.length; i++) {
numArr[i] = (int)(Math.random() * 10);
}
System.out.println(Arrays.toString(numArr));
for (int i = 0; i < numArr.length; i++) {
counter[numArr[i]]++;
}
for (int i = 0; i < numArr.length; i++) {
System.out.printf("%d의 개수: %d%n", i, counter[i]);
}
}
}