| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
- jvm
- backend
- java
- Docker
- Redis
- 개발자
- 티스토리챌린지
- data
- s3
- aws
- orm
- spring
- backenddeveloper
- jpa
- MSA
- nosql
- Spring Boot
- 기본형
- 오블완
- 자격증
- bootcamp
- CodeCommit
- goorm
- Cache
- mapping
- 스터디
- serverless
- QueryDSL
- goorm x kakao
- 자바
- Today
- Total
gony-dev 님의 블로그
JAVA 배열을 모르겠다고? 알려드리겠습니다! 본문
이전 시간에는 기본형 타입의 실수, 정수형에 대해 형변환하는 원리와 실습을 학습해보았다.
이번 시간에는 프로그래밍을 하며 결코 빠질 수 없는 배열에 대해 알아보자!
배열 자료형이란?
배열은 이전 시간까지 배웠던 Primitive Type이 아닌 Reference Type에 해당하는 자료형으로,
하나의 타입에 대해 여러 데이터들을 모아 구조적으로 다룰 수 있게 한다.
구성하는 각각의 값을 배열 요소(element)라고 하며, 위치를 가리키는 숫자를 인덱스(index)라고 한다.
배열을 생성해보자!
int[] nums = new int[100]; // int형 배열 생성
nums[0] = 1;
nums[1] = 2;
nums[2] = 3;
char[] chArr = new char[100]; // char형 배열 생성
chArr[0] = 'a';
chArr[1] = 'b';
위의 코드는 자바 프로그램에서 int와 char형의 배열을 각각 생성하는 코드이다.
자바의 배열은 선언 시, 고정된 크기를 가져야 하며 배열 길이를 반드시 지정해야 한다!
수동으로 선언하고 초기화해야 한다는 단점이 있기 때문에 배열의 길이를 처음부터 지정하지 않으면 컴파일 오류가 발생한다.
이렇게 생성한 배열은 인덱스 번호를 통해 특정 데이터에 접근하여 데이터 삽입 또는 수정 작업을 실행할 수 있다.
배열 출력
배열에 데이터를 넣어주면서 테스트나 결과물을 보기 위해 배열 내 데이터를 출력하고 싶을 때가 있다.
int[] nums = {1, 2, 3, 4, 5};
System.out.println(nums);
// 출력 결과 : [I@3fee733d
이렇게 배열 변수를 출력해보면 원하던 결과는 나오지 않는다.
위의 결과처럼 이상한 값이 나오게 되는데, 이것은 배열의 주소값을 의미한다.
"그렇다면 내가 원하는 결과는??"
배열 내 데이터를 출력하고 싶다면 보통 두 가지 방법을 많이 사용한다.
- for문을 사용하여 배열 순회
- Arrays.toString() 메서드 사용
import java.util.Arrays;
public class MyClass {
int[] nums = {1, 2, 3, 4, 5};
for(int i=0;i<nums.length;i++){
System.out.println(nums[i]);
}
System.out.println(Arrays.toString(nums));
}
/* 출력 결과
1
2
3
4
5
[1, 2, 3, 4, 5];
/*
예외적으로, char형 배열은 println()으로 바로 출력이 가능하다.
배열 복사
앞서 말했듯 자바에서 배열은 선언 시 고정된 크기를 가진다.
그렇다면 원래 계획했던 것보다 배열 공간이 추가로 필요하다면 어떻게 해야할까?
기본적으로 세 가지의 해결방법이 제공된다.
- 기존의 배열을 새로 만든 배열에 복사
- Arrays.copyOf() 메서드 사용
- System.arraycopy() 메서드 사용
하나씩 살펴보자.
1. 기존 배열을 새로 생성한 배열에 복사
public class MyClass {
public static void main(String[] args){
int[] nums = {1, 2, 3, 4, 5};
int[] newArr = new int[nums.length * 2];
for(int i=0;i<nums.length;i++){
newArr[i] = nums[i];
}
nums = newArr;
}
}
새 배열을 생성하고, for-loop를 통해 기존 배열의 값들을 새 배열에 넣어주었다.
그리고 원래의 배열을 가리키던 nums가 새로 생성된 newArr을 가리키도록 지정하였다.
사실 이 방법은 3가지 방법 중 성능이 가장 떨어진다.
그렇기 때문에 복사하면서 추가 작업이 필요하거나 실습용으로 사용하는 것을 권장한다.
2. System.arraycopy() 메서드 사용
public class MyClass {
public static void main(String[] args) {
int[] nums = {1, 2, 3, 4, 5};
int[] newArr = new int[nums.length * 2];
// System.arraycopy(복사할 배열, 복사를 시작할 배열 인덱스 값, 복사받을 배열, 복사된 배열값들이 붙여질 시작위치, 지정된 길이만큼 값들이 복사)
System.arraycopy(nums, 0, newArr, 0, nums.length);
}
}
System.arraycopy() 메서드는 방법들 중 가장 빠르며, 부분 복사 지원이 명확하다는 특징이 있다.
기본형 타입 뿐만 아니라 참조형 타입에서도 사용이 가능하지만, 5개의 인자를 정확히 입력하는 것을 주의해야 한다.
3. Arrays.copyOf() 메서드 사용
import java.util.Arrays;
public class MyClass {
public static void main(String[] args){
int[] nums = {1, 2, 3, 4, 5};
// Arrays.copyOf(복사할 배열, 할당받을 배열 길이);
int[] newArr = Arrays.copyOf(nums, nums.length * 2);
System.out.println(Arrays.toString(newArr));
}
}
/* 출력결과
[1, 2, 3, 4, 5, 0, 0, 0, 0, 0]
*/
Arrays.copyOf() 메서드는 기존의 배열을 특정 길이만큼 복사하여 새로운 배열에 할당하는 메서드이다.
가장 간결하고 가독성이 좋으며, 새로운 배열의 길이가 더 길다면 나머지는 기본값으로 채워진다.
깔끔한 코드를 구현하고 싶다면 제일 권장되는 방식이다.
배열 정렬
무작위로 배치된 배열 값들을 Arrays.sort()를 이용해 정리할 수 있다.
코드를 통해 알아보자.
import java.util.Arrays;
public class MyClass {
public static void main(String[] args) {
int[] nums = {6, 3, 2, 5, 4};
// 오름차순
Arrays.sort(nums);
System.out.println(Arrays.toString(nums));
// 부분 정렬
int[] arr = {9, 1, 4, 2, 6};
Arrays.sort(arr, 0, 3);
System.out.println(Arrays.toString(arr));
}
}
/* 출력결과
[2, 3, 4, 5, 6]
[1, 4, 9, 2, 6]
*/
복사와 마찬가지로 Arrays 메서드를 사용하면 정렬이 가능하다.
하지만 여기서 내림차순 구현은 보이지 않는데 이는 자바 기본형 타입에 대한 내림차순 정렬 메서드가 존재하지 않기 때문이다.
그래서 오름차순 정렬 후 뒤집거나 Integer 같은 객체 타입을 사용하여 Comparator 클래스를 사용하면 된다.
(이 부분은 후에 다루어질 예정이다.)
import java.util.Arrays;
public class MyClass {
public static void main(String[] args){
int[] nums = {6, 3, 2, 5, 4};
Arrays.sort(nums); // 오름차순 정렬
// 오름차순 정렬 후 뒤집기 구현
for(int i=0, j = nums.length-1; i<j; i++, j--) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
}
}
배열 비교
만일 두 배열을 서로 비교하고 싶다면 두 가지 방안 중 하나를 사용하면 된다.
- for-loop를 통해 순회하여 비교
- Arrays.equals() 메서드 사용
import java.util.Arrays;
public class MyClass {
public static void main(String[] args) {
int[] nums = {6, 3, 2, 5, 4};
int[] nums2 = {6, 3, 2, 5, 4};
// 1. for-loop를 통해 배열 순회
for(int i = 0; i<nums.length; i++){
if(nums[i] != nums2[i]){
System.out.println("Not equal to " + nums[i] + " and " + nums2[i]);
return;
}
}
// 2. Arrays.equals() 메서드로 배열 비교
System.out.println(Arrays.equals(nums, nums2)); // 배열 내 인덱스마다의 요소들이 같으므로 true
Arrays.sort(nums);
System.out.println(Arrays.equals(nums, nums2)); // nums의 오름차순 정렬 후 요소들이 인덱스마다 같지 않으므로 false
}
}
두 방법 모두 성능은 비슷하지만, 동작이나 안정성의 차이가 있다.
for-loop의 경우, 일부 구간만 비교가 필요한 경우나 특정 규칙을 적용하는 커스텀 방식을 이용할 수 있지만
코드 일부분 누락이나 인덱스 실수로 인해 비교적 버그가 나기 쉽다.
Arrays.equals()의 경우에는 배열 요소들이 같은지에 대한 유무가 명확하며 동일성 체크를 깔끔하게 처리한다.
또한 타입별로 정의가 명확하기 때문에 "두 배열이 같은지"만 비교한다면 Arrays.equals() 메서드를 사용하는 것을 권장한다.
다차원 배열
지금까지 배운 배열은 모두 1차원 배열이다.
배열은 좀더 고차원적으로 설계하는 것이 가능한데 2차원 이상의 배열을 정의하는 것을 다차원 배열이라고 하며,
배열 요소로 배열을 갖는 배열이라고 생각하면 된다.
2차원 배열
2차원 배열은 점에서 선이 되는 것처럼 1차원 배열이 모여 만들어지는 개념이라고 보면 된다.
한 학생의 과목 점수를 배열에 담기 위해서는 {90, 80, 80, 85} 처럼 1차원 배열을 생성하면 된다.
그러면 여러 명의 학생을 담기 위해서는 어떻게 하면 될까? 2차원 배열을 생성하면 된다.
2차원 배열은 테이블 형태의 데이터를 적재하기 위해 사용되며, 행(row)과 열(column)의 개념만 알면된다.
| 국어 | 영어 | 수학 | |
| 철수 | 90 | 85 | 90 |
| 영희 | 70 | 90 | 90 |
| 영수 | 85 | 100 | 80 |
위의 표가 곧 테이블이며, 이를 2차원 배열로 생성하면 다음과 같다.
import java.util.Arrays;
public class MyClass {
public static void main(String[] args) {
int[][] students = {{90, 85, 90}, {70, 90, 90}, {85, 100, 80}}; // 학생당 국영수 점수
for(int i=0; i<students.length; i++){
System.out.println("학생" + (i+1) + " 의 국영수 점수는 " + Arrays.toString(students[i]));
}
// or
System.out.println(Arrays.deepToString(students);
}
}
/*
학생1 의 국영수 점수는 [90, 85, 90]
학생2 의 국영수 점수는 [70, 90, 90]
학생3 의 국영수 점수는 [85, 100, 80]
[[90, 85, 90], [70, 90, 90], [85, 100, 80]]
*/
위의 코드는 선언과 동시에 데이터를 넣어주었지만,
사전에 초기화를 진행하고 싶다면, 1차원 배열 초기화에 대괄호를 한 번 더 추가하면 된다.
이렇게 new int[3][3]이라는 첫번째 괄호는 행 개수를, 두번째 괄호는 열의 개수를 나타내는 배열이 생성된다.
출력방법에는 2가지가 있는데 1차원 배열 출력과 비슷하다.
- for-loop를 이용해 배열 내 배열 출력
- Arrays.deepToString() 메서드를 사용
가변 배열
배열은 기본적으로 고정된 길이를 가지고 있어야 하는 것이 맞다.
2차원 배열 역시 테이블 형태라고 해서 가로와 세로의 길이가 모두 같아야 할 것 같은데, 그렇지 않다.
자바에서 다차원 배열은 각 요소로 들어가는 1차원 배열의 길이를 각기 다르게 해도 2차원 배열을 생성하는데 문제가 없다.
(물론 2차원 배열이 아닌 다차원 배열도 가능하다!)
import java.util.Arrays;
public class MyClass {
public static void main(String[] args) {
int[][] score = {
{100, 100, 100, 100},
{20, 20, 20},
{30, 30},
{40, 40},
{50, 50, 50}
};
System.out.println(Arrays.deepToString(score));
}
}
/* 출력결과
[[100, 100, 100, 100], [20, 20, 20], [30, 30], [40, 40], [50, 50, 50]]
*/
객체 배열
지금까지 기본형 타입들에 대해 배열을 생성해보았다.
이제 객체 타입을 배열에 넣어 사용하는 방법에 대해 알아보자.
객체 또한 하나의 자료형으로 취급되기에 배열을 생성하는데 문제가 없다.
다만 알아두어야 하는 점은 객체 타입은 참조를 사용한다는 점을 유의해야 한다.
아래 코드를 살펴보자.
import java.util.Arrays;
public class MyClass {
public static void main(String[] args) {
class Obj{
String str;
int num;
public Obj(String str, int num) {
this.str = str;
this.num = num;
}
}
Integer[] arr = {1, 3, 5, 7};
System.out.println("Integer Array : " + Arrays.toString(arr));
Obj[] objArr = {new Obj("str1", 1), new Obj("str2", 2)};
System.out.println("Obj Array : " + Arrays.toString(objArr));
}
}
/* 출력 결과
Integer Array : [1, 3, 5, 7]
Obj Array : [MyClass$1Obj@4617c264, MyClass$1Obj@36baf30c]
*/
다음과 같이 두 객체 타입(Integer, Obj)에 대해 배열을 생성하고 출력해보았다.
Integer에 대해서는 실제값이 잘 출력되는 반면, Obj에 대해서는 주소값 형태가 출력되었다.
이와 같이 서로 다른 값의 타입이 출력되는 것은 toString에 대한 메서드 유무에 따라 달라지기 때문이다.
Integer 같은 래퍼 클래스는 toString()이 내부에서 이미 구현되어 있어 실제 값이 출력되지만,
Obj는 toString()이 구현되어 있지 않아 저런 형태가 출력되는 것이다.
얼핏 보면 주소값 같지만 정확히는 "해시 기반 식별 문자열"이라는 점을 알아두자.
객체 배열 복사
import java.util.Arrays;
public class MyClass {
public static void main(String[] args) {
Obj[] objArr = {new Obj("str1", 1), new Obj("str2", 2)};
System.out.println("Obj Array : " + Arrays.toString(objArr));
Obj[] objArr2 = objArr.clone();
System.out.println("Obj Array2 : " + Arrays.toString(objArr2));
}
}
/* 출력 결과
Obj Array : [MyClass$1Obj@5acf9800, MyClass$1Obj@4617c264]
Obj Array2 : [MyClass$1Obj@5acf9800, MyClass$1Obj@4617c264]
*/
객체 타입으로 이루어진 배열도 복사가 가능하다!
하지만 출력 결과를 보면 식별 문자열이 같은 것을 알 수 있다.
이는 실제값의 복사와 함께 참조값이 함께 복사되었기 때문이다.

(배열 변수명).clone() 메서드는 배열만 새로 만들고 배열 안의 원소는 그대로 같은 객체를 가리키는 참조값을 복사한다.
그래서 출력 시 각 원소가 같은 객체를 가리키기 때문에 똑같은 문자열이 출력되는 것이다.
이런 경우, 한쪽 배열에서 원소 객체를 수정하면 다른 배열에서도 객체가 수정되기 때문에
Obj 객체 자체를 새로 생성하여 넣어주는 것을 권장한다.
객체 배열 정렬
객체 배열에서 객체들을 정렬하기 위해서는 Arrays.sort() 메서드만으로 정렬되지 않는다.
Obj 클래스만 해도 str과 num 중 어느 것을 정렬할 것인지에 따라 정렬 기준이 달라지기 때문에
이러한 경우 Comparable 인터페이스와 Comparator 클래스를 이용한 객체 배열 정렬 방법을 사용한다.
1. Comparable 인터페이스
자바에서 같은 타입의 인스턴스를 서로 비교해야 할때 Comparable 인터페이스를 구현하여 compareTo() 메서드를 재정의한다.
import java.util.Arrays;
public class MyClass {
public static void main(String[] args) {
class Obj implements Comparable<Obj> {
String str;
int num;
public Obj(String str, int num) {
this.str = str;
this.num = num;
}
@Override
public int compareTo(Obj o) {
if(this.num > o.num) {
return 1;
} else if(this.num == o.num) {
return 0;
} else {
return -1;
}
}
}
Obj[] objArr = {new Obj("str1", 9), new Obj("str2", 2), new Obj("str3", 4)};
System.out.println("정렬 전");
for (Obj obj : objArr) {
System.out.println("str : " + obj.str + " num : " + obj.num);
}
Arrays.sort(objArr);
System.out.println("정렬 후");
for(Obj obj : objArr) {
System.out.println("str : " + obj.str + " num : " + obj.num);
}
}
}
/* 출력 결과
정렬 전
str : str1 num : 9
str : str2 num : 2
str : str3 num : 4
정렬 후
str : str2 num : 2
str : str3 num : 4
str : str1 num : 9
*/
compareTo 메서드의 비교는 -1, 0, 1로 결정된다.
반드시 -1, 0, 1로 나눌 필요는 없으며, 두 값을 비교하여 나온 결과에 따라 값을 반환해도 된다.
- this.num > o.num | 양수 -> this가 뒤
- this.num == o.num | 0 -> 정렬 x
- this.num < o.num | 음수 -> this가 앞
하지만 프로젝트에 적용하거나 코딩 테스트 시에는 오버플로우의 위험 때문에 Integer.compare 메서드를 사용하는 것을 권장한다.
2. Comparator 클래스
Comparator 클래스를 통해 객체 정렬이 가능하다.
import java.util.Arrays;
import java.util.Comparator;
public class MyClass {
public static void main(String[] args) {
class Obj {
String str;
int num;
public Obj(String str, int num) {
this.str = str;
this.num = num;
}
}
Obj[] objArr = {new Obj("str1", 9), new Obj("str2", 2), new Obj("str3", 4)};
System.out.println("정렬 전");
for (Obj obj : objArr) {
System.out.println("str : " + obj.str + " num : " + obj.num);
}
Comparator<Obj> byNumAsc = new Comparator<>() {
@Override
public int compare(Obj a, Obj b) {
return Integer.compare(a.num, b.num);
}
};
/* 람다 표현식으로 교체
Comparator<Obj> byNumAsc = (a, b) -> Integer.compare(a.num, b.num);
-> 메서드로 교체
Comparator<Obj> byNumAsc = Comparator.comparingInt(a -> a.num);
*/
Arrays.sort(objArr, byNumAsc);
System.out.println("정렬 후");
for(Obj obj : objArr) {
System.out.println("str : " + obj.str + " num : " + obj.num);
}
}
}
/* 출력 결과
정렬 전
str : str1 num : 9
str : str2 num : 2
str : str3 num : 4
정렬 후
str : str2 num : 2
str : str3 num : 4
str : str1 num : 9
*/
Compareable에서 설명했던 compareTo() 메서드를 사용하여 Compartor를 구현했다.
지금은 직접 구현을 했지만 주석처리한 코드를 보면 람다로 더 짧게 코딩하거나 메서드만으로도 구현이 가능하다!
내림차순은 Comparator에 reversed 메서드만 추가하면 되니 어렵게 생각하지 말고 구현해보자!!
출처
1- Inpa Dev
'JAVA' 카테고리의 다른 글
| 추상 클래스? 알려드리겠습니다! (2) | 2026.01.20 |
|---|---|
| JAVA의 꽃, 객체 지향 프로그래밍(OOP)의 클래스 문법 (1) | 2026.01.12 |
| JAVA 타입 형변환 (1) | 2026.01.04 |
| JAVA 기본형 데이터 타입 (0) | 2025.12.31 |
| JAVA 변수 종류 (0) | 2025.12.30 |