java 에서는 Array 와 ArrayList 는 컬렉션 데이터를 저장하는데 자주 사용되는 기본적인 데이터 구조이다. 모두 동일한 용도로 사용되지만 서로 다른 특성으로 인해 애플리케이션 성능과 유연성에 영향을 미치는 부분이 있다.
이번 포스팅에서는 자바의 Array 와 ArrayList 의 주요 기능 및 각각의 장단점과 두 구조를 변환하는 여러 기법을 소개한다.
Arrays
Array(배열) 동일 데이터 타입의 요소를 연속된 메모리 위치에 저장하는 [고정 크기]를 가지는 데이터 구조이다. 배열의 각 요소는 index 로 검색할 수 있으며 첫 번째 요소는 index 0 부터 시작한다.
- 배열은 항상 동일한 데이터 타입의 요소를 저장한다. 배열 타입은 초기화할 때 고정 사이즈로 선언된다.
- 각각의 요소들은 index 를 통해서만 액세스 할 수 있다. 배열의 요소에 액세스 할 수 있는 다른 방법은 없다.
- 배열의 크기는 항상 고정되어 있으며 변경할 수 없다. 배열 크기보다 더 많은 요소를 저장하기 위해서는 새 배열을 생성하고 이전 배열의 요소들을 새로운 배열에 복사해야 한다. 배열 크기 보다 더 많은 요소를 추가하려고 하는 경우 ArrayIndexOutOfBoundsException 이 발생한다.
// 배열 선언 및 초기화
int[] a = new int[4];
a[0] = 1;
a[1] = 2;
a[2] = 4;
a[3] = 8;
for(int i = 0; i < a.length; i++) {
System.out.println(a[i]);
}
==========
결과
1
2
4
8
변수 a array 는 아래와 같은 구조로 데이터가 저장된다.
a[0] | a[1] | a[2] | a[3] |
1 | 2 | 4 | 8 |
ArrayList
ArrayList는 자바 컬렉션 프레임워크에 속해 있으며 List 인터페이스를 구현한다. 배열과 달리 ArrayList 는 요소가 추가되거나 제거될 때 동적으로 늘어나거나 줄어들 수 있다.
ArrayList 는 여러 타입의 요소를 저장할 수 있지만 요소를 가져올 때 런타임에 ClassCastException 이 발생할 수 있으므로 대부분 권장하지는 않는다. 타입 안전성을 강화하기 위해 제너릭을 사용하여 ArrayList 를 선언한다.
List<Integer> a = new ArrayList<>();
a.add(1);
a.add(2);
a.add(4);
a.add(8);
===================
for(Integer i : a) {
System.out.println(i);
}
========
결과
1
2
4
8
===================
ListIterator<Integer> listIterator = a.listIterator();
while (listIterator.hasNext()) {
System.out.println(listIterator.next());
}
========
결과
1
2
4
8
===================
a.forEach(System.out::println);
========
결과
1
2
4
8
ArrayList 는 for-loop 을 사용하여 순차적으로 접근하는 방법 이외에도 ListIterator 와 같은 iterator 를 사용하여 각 요소에 접근하거나 forEach(Consumer<? super T> action) 을 통해서도 접근할 수 있다.
Array 와 ArrayList 비교
Array | ArrayList | |
크기 | 초기화에 지정된 크기로 고정 | 동적으로 resize 됨 |
메모리 | 배열 크기가 요소의 수보다 큰 경우 낭비 초래 | 동적 크기 조정으로 약간의 성능 오버헤드 발생 |
성능 | 읽기 작업의 경우 인덱스를 사용하여 요소에 직접 액세스 하기 때문에 ArrayList 보다 빠르지만 크기 조정이 필요한 쓰기 작업의 경우 ArrayList 가 성능이 좋음 | 크기 조정이 필요한 쓰기 작업을 제외하고 ArrayList 는 배열보다 성능이 떨어짐 |
적합한 용도 | 고정된 크기의 컬렉션이 필요하고 메모리 효율이 중요한 경우에 사용 | 작고 미미한 성능 향상 보다 편의성이 우선시되는 소규모 컬렉션에 사용 |
Array 를 ArrayList 로 변환
샘플 코드는 JDK 17 로 작성되었습니다.
//mutable arraylist 로 변환
//arrayList 에 요소를 추가하거나 삭제할 수 있음
String[] sports = {"baseball", "soccer", "basketball"};
ArrayList<String> arrayList = new ArrayList<>(Arrays.asList(sports));
sportList.add("running");
sportList.forEach(System.out::println);
//immutable arraylist 로 변환
String[] sports = {"baseball", "basketball", "soccer"};
List<String> sportList = Arrays.asList(sports);
sportList.add("running"); // -> UnsupportedOperationException 발생
sportList.forEach(System.out::println);
java8 에 도입된 stream 을 사용하여 변환할 수 있습니다.
String[] sports = {"baseball", "basketball", "soccer"};
//immutable arraylist 로 변환
List<String> sportList = Arrays.stream(sports).toList();
sportList.forEach(System.out::println);
ArrayList 를 Array 로 변환
ArrayList 의 toArray 를 통해 변환한다.
List<String> arrayList = Arrays.asList("baseball", "basketball", "soccer");
//방법1
String[] array1 = arrayList.toArray(new String[0]);
//방법2
Object[] array2 = arrayList.toArray();
//방법3
String[] array3 = arrayList.toArray(new String[4]);
방법1은 요소가 없는 빈 String 배열을 파라미터로 넘겨주어 String[] 로 변환하였다. new String[0] 을 인자로 넘겨준 이유는 빈 배열 타입을 생성하도록 하여 메모리를 절감하기 위해서이다. new String[arrayList.size()] 를 넘기지 않는 이유는 불필요한 메모리 사용 때문이다. 즉, new String[0] 을 넘기나 new String[arrayList.size()] 를 넘기나 toArray 의 결과는 동일하다.
방법2는 아무런 파라미터도 넘기지 않으나 Object[] 이 리턴되기 때문에 형변환을 해줘야 한다.
방법3은 ArrayList 보다 사이즈가 큰 배열을 파라미터로 넘겼을 때 ArrayList 의 각 요소는 배열에 복사되지만 ArrayList 보다 더 큰 index 의 값에는 null 이 채워짐을 보여주기 위해서 추가하였다.
지금까지 Array와 ArrayList 에 대해서 알아 보았다.
이 포스팅을 읽으시는 분에게 도움이 되었기를 바랍니다.
'자바' 카테고리의 다른 글
jdk pattern matching for instanceof (0) | 2023.08.26 |
---|---|
java switch expression - from jdk 14 (0) | 2023.08.20 |
java record 용법 - from jdk 14 (0) | 2023.08.19 |
CompletableFuture 를 알아보자 (0) | 2023.08.06 |
byte 배열에서 charset 정보 detecting 하기 (0) | 2023.08.04 |
댓글