본문 바로가기
Java

Java 리스트(List) 컬렉션 종류 ArrayList, Vector, LinkedList

by jayden-lee 2019. 4. 22.
728x90

List 컬렉션의 종류로는 ArrayList, Vector, LinkedList가 있다. 애플리케이션 개발 업무를 하면서 List 컬렉션을 많이 사용한다. 특히 ArrayList를 많이 사용하고, 가끔 Queue 자료구조를 사용할 때만 LinkedList를 사용했다.

List 컬렉션

List 컬렉션은 객체를 일렬로 늘어놓은 구조로 이루어져 있다. 객체를 인덱스로 관리하기 때문에 List 컬렉션에 객체를 추가하면 자동 인덱스가 부여된다. 인덱스는 객체를 검색, 삭제할 때 사용한다. List 컬렉션은 객체 자체를 저장하는 것이 아닌 객체의 번지를 참조한다.

 

동일한 객체를 저장하는 것을 허락(이 점은 Set 인터페이스와 다른점)하는데, 이 경우에는 동일한 객체의 번지를 참조한다. 그리고 null도 저장할 수 있다.

 

List 컬렉션 인터페이스에는 객체를 추가(add), 검색(contain, get, size), 삭제(remove, clear) 기능의 메서드가 선언되어 있다. 그러므로 List 컬렉션 인터페이스를 상속해서 구현하는 클래스는 해당 기능을 오버라이드 해야한다.

 

ArrayList 객체를 생성해서 객체를 추가, 검색, 삭제하는 과정을 보여주는 예제 코드이다.

// String 객체를 관리하는 ArrayList 생성
List<String> list = new ArrayList();

// String 객체 저장
list.add("Hello World");

// null 저장
list.add(null); 

// 동일한 String 객체를 갖고 있는지 검색 
boolean isFindValue = list.contains("Hello World");

// 인덱스 값을 이용하여 객체 삭제
list.remove(0);

// List에 저장된 모든 객체를 얻어서 콘솔 창에 출력 
for(String value : list) {
    System.out.println(value);
}

 

List 컬렉션에 대해 알아보았으니, 이제 ArrayList, Vector, LinkedList에 대해 하나씩 알아보자.

ArrayList 클래스

ArrayList는 List 컬렉션 인터페이스를 구현한 클래스이다. List 컬렉션처럼 인덱스로 객체를 관리한다. 일반 배열과 ArrayList는 인덱스로 객체를 관리한다는 점에서 동일하지만, 크기를 동적으로 늘릴 수 있다는 점에서 차이점이 있다.

 

배열은 처음 생성할 때 고정 크기를 지정하며, 사용 중에 크기를 변경할 수 없다. 만약 크기를 늘려야 한다면 새로 배열을 선언해야 한다는 단점이 있다. 정해진 크기가 있다면 좋겠지만 매번 바뀌는 요구사항에 따라 동적으로 유연하게 처리해야 한다면, 배열보다는 ArrayList를 사용하는 것이 정신 건강에 이롭다.

 

ArrayList는 내부에서 처음 설정한 저장 용량(capacity)가 있다. 설정한 저장 용량 크기를 넘어서 더 많은 객체가 들어오게 되면, 자동적으로 저장 용량이 늘어난다.

 

ArrayList 클래스의 기본 저장 용량

// String 객체를 관리하는 ArrayList 생성
// 기본 저장 용량은 10
List<String> list = new ArrayList<>();

// 저장 용량을 100으로 설정해서 String 객체를 관리하는 ArrayList 생성
List<String> list = new ArrayList<>(100);

 

List 컬렉션 인터페이스는 제네릭 타입이다. 이전 버전의 호환을 위해서 타입 파라미터 없이 아래 코드처럼 ArrayList를 생성할 수 있다.

// java 4 이전에서 ArrayList 생성 방법
// 타입 파라미터를 지정하지 않았으므로 IDE에서 경고를 표시합니다.
List list = new ArrayList();

 

하지만, 이렇게 선언하게 되면 어느 객체든 저장할 수 있는 ArrayList가 되어 버린다. 이유는 객체가 저장될 때 Object 타입으로 변환되어 저장되기 때문이다. Object 클래스는 모든 클래스의 조상(부모) 클래스이기 때문이다. 모든 종류의 객체를 저장할 수 있다는 것은 장점입니다. 다만, ArrayList 내부에 있는 객체(Object)를 꺼내서 사용할 때 실제 타입으로 변환해야 하므로 실행 성능에 좋지 못한 영향을 끼치게 된다.

 

ArrayList가 앞에서 배열과 유사하다고 설명했다. ArrayList에서 특정 인덱스의 객체를 제거하게 되면, 제거한 객체의 인덱스부터 마지막 인덱스까지 모두 앞으로 1칸씩 앞으로 이동한다. 객체를 추가하게 되면 1칸씩 뒤로 이동하게 된다. 인덱스 값을 유지하기 위해서 전체 객체가 위치가 이동한다.

 

이처럼 객체 삭제와 삽입이 빈번하게 일어나는 곳에서 ArrayList를 사용하지 않는 것이 좋다. 이러한 경우에는 LinkedList를 사용하는 것이 좋다. ArrayList는 인덱스가 있으므로 객체 검색, 맨 마지막에 객체 추가 등에서 좋은 성능을 발휘한다.

 

  • ArrayList : 객체 검색, 맨 마지막 인덱스에 객체 추가에 좋은 성능을 발휘함
  • LinkedList : 객체 삽입 및 삭제에 좋은 성능을 발휘함
// ArrayList 객체 생성
List<String> list = new ArrayList<>();

// 객체 추가
list.add("Hello");
list.add("gglee");

// 객체 총 개수
int size = list.size();

// 동일한 객체 있는지 검색
boolean isFindValue = list.contains("gglee");

// 인덱스에 위치하는 객체 값 얻기
String str = list.get(0);

// 인덱스를 이용하여 객체 삭제
list.remove(0);

 

고정된 객체들로 구성된 List를 생성하는 방법도 있다. Arrays 클래스의 정적 메서드 asList()를 이용하면 된다. 이 정적 메서드를 이용하여 생성된 ArrayList는 앞에서 설명한 ArrayList와는 다른 클래스이다. 이 ArrayList는 추가(add), 삭제(remove) 기능을 제공하지 않는다.

// String 객체를 관리하는 ArrayList 생성
List<String> list = Arrays.asList("hello", "gglee", "java");

// UnsupportedOperationException 에러 발생
// list.add("hello");

// UnsupportedOperationException 에러 발생
// list.remove(1);

Vector 클래스

Vector는 ArrayList와 동일한 내부 구조를 가지고 있다. Vector 객체를 생성하기 위해서는 저장할 타입을 지정해야 한다. ArrayList와 차이점으로는 Vector 클래스는 동기화된(synchronized) 메서드로 구성되어 있다. 그렇기 때문에 멀티 스레드 환경에서 안전하게 객체를 추가, 삭제할 수 있다. 즉, 스레드에 안전하다(Thread Safe)라고 말합니다. 다만 동기화되어 있기 때문에 ArrayList 보다는 객체를 추가, 삭제하는 과정은 느릴수 밖에 없다. 안전성을 추구하는데 있어서 속도를 포기한 트레이드 오프(trade off)이다.

 

Vector 클래스의 add() 동기화 메서드

Vector 객체를 생성하는 예제 코드이다.

// String 객체를 관리하는 Vector 객체 생성
List<String> list = new Vector<>();

// 객체 추가
list.add("hello");

// 객체 제거
list.remove(0);

LinkedList 클래스

LinkedList는 List 구현 클래스이다. 내부 구조는 ArrayList와 다르다. ArrayList에는 내부 배열에 객체를 저장해서 인덱스로 관리하지만, LinkedList는 인접 참조를 링크해서 체인처럼 관리한다. 그렇기 때문에 LinkedList에서 특정 인덱스의 객체를 제거하게 되면, 제거되는 인덱스의 앞 뒤 링크만 변경되고 나머지 링크는 변경되지 않는다.

 

ArrayList는 제거되는 인덱스를 기준으로 뒤에 있는 객체가 한칸씩 이동 했었던 점과 차이가 있다. 이러한 차이로 인해서 객체를 삽입, 삭제하는 로직에 있어서 ArrayList보다 LinkedList를 사용할 때 좋은 성능이 나옵니다.

// LinkedList 객체 생성
List<String> list = new LinkedList<>();

댓글