자바

java Array vs ArrayList

알쓸개잡 2023. 8. 19.

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 에 대해서 알아 보았다.

 

이 포스팅을 읽으시는 분에게 도움이 되었기를 바랍니다.

댓글

💲 추천 글