김지팡의 저장소
Published 2024. 11. 5. 01:23
ArrayList의 동작원리 이해하기 TIL
728x90

 

이번 포스팅에서는 List 인터페이스의 구현체인 ArrayList의 동작 원리에 대해서 이야기해보고자 한다.

 

 

📌 ArrayList를 언제 사용할까?


기본적으로 자바에서 배열을 사용하면 크기를 지정해 주거나 값을 초기화해줘야 한다. 그래서, 크기를 지정해주지 못하거나 값을 초기화하지 못할 때, 배열을 사용할 수 없다. 이때 사용하는 것이 ArrayList이다.

 

ArrayList는 크기를 지정해주지 않아도 되고, 값을 저장할 수 있다.(내부적으로 ArrayList는 int 타입의 인덱스를 사용해 데이터를 관리하기 때문에 이론적으로는 최대 약 21억 개 까지 데이터 저장이 가능하다고 한다.)

 

 

📌 ArrayList의 크기는 가변적이다?


ArrayList는 배열과 달리 크기를 지정하지 않고 데이터를 삽입할 수 있기 때문에 가변적이라고 생각해 왔다.

 

기본적으로 ArrayList는 내부적으로 배열을 사용하고 있기 때문에 해당 이유만으로 가변적이라고 할 수 없었지만, ArrayList의 내부 동작 방식을 알고 나니 그렇지 않다는 것을 알았다.

 

 

📌 ArrayList 내부 동작원리


ArrayList는 내부적으로 배열을 사용하는데, 배열과 동일하게 초기 용량을 설정해야 한다. 여태껏 ArrayList를 사용하면서 크기를 지정하지 않고 사용했었는데, 그럴 수 있었던 이유는 기본값으로 지정되는 크기(10)가 존재했기 때문이었다.

 

10개의 데이터가 삽입되면, ArrayList 내부의 배열을 기존 크기의 1.5배 수준의 크기 배열을 생성하고, 기존 배열에 있는 요소들을 새로 생성한 배열에 복사한다.

 

이후, 기존 배열을 참조하고 있던 List 객체가 새로 생성한 배열을 참조하도록 하고, 기존 배열을 GC에 의해 메모리에서 해제된다.
이와 같은 방식으로 ArrayList는 자신의 크기를 증가시킨다.

 

 

📌 높은 성능 비용을 발생시키는 ArrayList 크기 증가 과정


위에서 말한 ArrayList의 크기를 증가시키는 과정은 배열을 새로 생성하고, 복사하는 작업이 필수적이기 때문에 데이터를 많이 넣을수록 성능을 저하시킨다는 단점이 있다.

 

때문에, 정확히 가늠은 못하더라도 많은 양의 데이터가 저장될 것이라면 ArrayList를 사용하는 것이 성능 저하의 원인이 될 수도 있다.

 

 

📌 ArrayList의 초기 크기를 설정할 수 있다.


ArrayList는 배열과 달리 초기에 크기를 설정하지 않아도 내부적으로 쓰이는 배열의 크기를 자동으로 늘려주는 것이 가능하지만, 성능 저하라는 단점이 있다고 이야기했다.

 

하지만, ArrayList의 초기 크기를 설정해 성능 저하를 예방할 수 있다.

List<Integer> list = new ArrayList<>(100);

 

ArrayList 타입 객체 list의 크기를 100으로 지정했다. 아무것도 작성하지 않으면 초기 크기는 10으로 할당되는 것이지만 100으로 지정함으로써 100개의 데이터가 삽입될 때까지는 ArrayList가 내부적으로 배열의 크기를 자동으로 증설하지 않는다.

즉, 배열의 크기를 자동으로 증설하는 과정에서 생기는 성능 저하가 사라지게 되는 것이다.

 

List<Integer> list = new ArrayList<>();
list.ensureCapacity(100);

 

초기 크기를 설정하는 것 말고도 ensureCapacity() 메서드를 활용해서 크기를 지정할 수 있는데, 둘은 확실한 차이점이 있다.

 

 

📌 ensureCapacity(100) & new ArrayList<>(100)


ensureCapacity()는 ArrayList의 크기를 한 번에 원하는 크기만큼 늘리는 것이다. 그렇기 때문에, 배열의 크기를 초과하려 할 때마다 자동으로 크기를 증설해 주는 여러 번의 과정에 비해 성능적으로 좋을 수밖에 없다.

 

그리고, ensureCapacity는 원할 때 메서드를 호출해서 크기를 늘리는 것이기에 초기부터 100이라는 메모리 공간을 확보하는 것과는 다르다.

 

예를 들어, 100 크기만큼 ArrayList 객체를 생성했다가 데이터를 그만큼 삽입하지 않는다면 낭비가 되기 때문이다.

 

하지만, ensureCapacity() 메서드를 호출해 배열의 크기를 늘리는 것도 한 번에 100으로 크기를 늘려주지만 배열을 생성하고 기존 배열의 데이터들을 새로운 배열로 복사하는 과정이 포함된다.

 

때문에, 만약 100개 정도의 데이터가 들어간다는 것을 확신한다면 ensureCapacity()보다는 초기 크기를 100으로 아예 선언하는 것이 낫다.

 

 

📌 마무리


ArrayList의 동작 방식에 대해 알고 나니 확실히 더 잘 사용할 수 있을 것이라는 생각이 들었다. 데이터의 크기를 확정할 수 있다면 초기 크기를 설정하는 것이 좋을 것 같고, 일련의 과정 속에서 조금씩 커지는 것이라면 ensureCapacity를 사용하는 것이 나은 선택인 것 같다.

728x90

'TIL' 카테고리의 다른 글

Set 이해하기  (0) 2024.11.08
LinkedList 이해하고 ArrayList와 비교하기  (3) 2024.11.05
의존성과 의존성 역전 이해하기  (3) 2024.11.01
순환 참조 이해해보기  (0) 2024.10.31
단일 책임의 원칙 (누구의 책임인가)  (0) 2024.10.08
profile

김지팡의 저장소

@김지팡

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!