ArrayList와 Vector
- List인터페이스로 접근할 수 있다.
- 동적 배열로서 새로운 데이터를 추가할 때 공간이 부족하면 늘려준다.
왜 Vector가 사실상 Deprecated되었을까?
간단히 요약하자면 다음과 같다.
- thread-safe를 지원하지만 다른 메소드로 대체가능하다.
- 속도가 느리다.
- 증가폭이 커서 공간을 많이 차지한다.
- legacy class이다.
1. Vector는 자체적으로 동기화기능을 제공한다.(thread-safe하다.)
HashTable, Vector같은 구버전 패키지는 자체적으로 동기화 기능을 제공한다.
Vector에서 thread-safe는 synchronized
예약어를 통해 지원한다.그래서 Vector객체에서 제공되는 메소드들은 synchronized
예약어가 붙어있다.
그러나 이 예약어의 문제는 속도가 느리다는 것이다. synchronized
는 mutex처럼 동작한다. synchronized
예약어가 걸린 메소드에 접근을 시도하면 해당 메소드를 가진 this객체에 대해 lock이 걸린다. 즉 Vector객체에 대해 lock이 걸린다. 이로 인하여 어떤 스레드가 들어가서 동작중이면 다른 스레드는 기존 스레드가 동작을 마칠 때까지 기다려야 한다.그래서 여러 스레드가 같은 Vector객체를 사용할 경우 속도가 느려질 수 있다. 이로 인해 스레드가 수정, 삭제, 조회 작업을 할 동안 다른 스레드가 읽기 작업을 하지 못해 전체 서비스 처리 속도가 저하될 수 있다(MSA의 CQRS패턴 참고).
따라서 Vector는 synchronized
를 이용해서 thread-safe를 보장하지만 multi thread환경에서 실행시킬 경우 속도가 저하되는 문제점이 발생한다.
참고: MSA의 CQRS패턴
MSA의 CQRS패턴은 읽기 역할을 하는 모델과 생성, 수정, 삭제 역할을 하는 모델을 분리시키는 패턴이다. 이 패턴은 서비스의 기능 대부분이 읽기 기능에 치중되어 있다는 이유로 만들어졌다. 읽기 작업이 생성, 수정, 삭제 작업에 의해 기다리면 서비스가 느려진다. 즉 읽기만 빠르게 처리할 수 있으면 서비스 처리 속도가 향상될 수 있으므로 읽기 부분을 분리시키고자 나온 패턴이다.
게시판을 서비스를 제공한다고 가정하자. 게시판에는 게시글을 쓰고, 수정하고, 삭제하고, 볼 수 있다. 이 중에서 가장 많이 이용하는 기능을 무엇일까? 바로 읽기 기능이다. 게시글을 쓰는 사용자는 1명이지만 게시글을 보는 사용자는 여러명이기 때문이다.
2. capacity 증가 폭이 크다
ArrayList와 Vector모두 동적배열이므로 데이터가 추가할 때마다 공간의 크기에 따라 capacity를 증가한다. ArrayList는 기존 capacity의 1.5배를 증가하며 Vector는 기존 capacity에 2배를 증가한다. 즉 Vector이 증가폭이 더 크다. 이로 인해 Vector가 ArrayList보다 더 많은 공간을 낭비한다.
3. Vector의 thread-safe는 다른 방법으로 대체가능하다
대부분의 서비스가 읽기 기능에 치중되어 있더라도 무결성을 이유로 thread-safe하게 지원하는 Vector객체의 사용에 고민을 할 수 있다. 그러나 thread-safe는 Vector객체만 제공하지 않는다. Collections.synchronizedList
메소드로 thread-safe를 지원하지 않는 리스트를 thread-safe를 지원하도록 만들 수 있다.
다음의 예시는 thread-safe를 지원하지 않는 ArrayList를 thread-safe를 지원하도록 만드는 코드이다.
List<Integer> threadSafeArrayList = Collections.synchronizedList(new ArrayList<>());
이 코드의 결과물도 synchronized
를 이용해 동기화를 한다.
그럼 Vector는 왜 남아있을까?
Vector는 성능도 좋지 않고 같은 기능을 수행하면서 성능이 좋은 ArrayList로 대체가 가능한 legacy class이다. 그럼 왜 Vector를 삭제하지 않을까? 바로 하위호환성 때문이다. Vector를 제거할 경우 Vector과 연관된 모든 기능들을 수정해야 한다. 오래된 시스템일 수록 결합도가 높아서 수정하기 어려울 수도 있다.
Vector와 ArrayList의 가장 큰 차이점은 Enumeration을 이용한 순회를 지원한다는 것이다. Enumeration은 Iterator가 나오기 전에 제공하던 순회방법이다. ArrayList는 이 기능을 지원하지 않지만 Vector는 이 기능을 지원한다. 즉 Vector에 의존된 기능을 수정하는 것은 Vector를 순회하는 Enumeration도 찾아서 바꿔야 한다.
따라서 Vector를 대체할 수 있는 ArrayList가 나오기 전에 Vector로 만들어놓은 프로그램들도 수행하기 위해 남겨놓았다.
정리
- ArrayList와 Vector는 동적 배열이라는 공통된 기능을 제공한다.
- Vector은
synchronized
예약어로 thread-safe로 보장한다. - ArrayList는 thread-safe를 보장하지 않는다.
- Vector는 thread-safe를 지원하여 속도는 느리다.
- Vector의 thread-safe의 기능은
Collections.synchronizedList
메소드로 대체 가능하다. - Vector는 capacity 증가분이 2배이지만 ArrayList는 capacity 증가분이 1.5배이다.
- Vector는 하위 호환성 때문에 존재하고 현재는 잘 사용되지 않는다.