컬렉션 프레임워크
자바에서 효율적인 자료구조를 사용할 수 있는 컬렉션들을 알아보자
컬렉션 프레임워크
List
ArrayList
특징
배열을 쓰지만, 데이터가 꽉 차면 자동으로 더 큰 배열을 만들어 복사함으로써 동적으로 크기가 조절되는 것처럼 동작한다.
일반적으로 정적 배열을 사용하는 경우 데이터의 크기를 고정해야 하기 때문에 동적으로 관리하기 불편함을 개선한 자료구조이다.
동기화를 지원하지 않는다.
성능
장점 - O(1)
인덱스 기반 조회:
get(index)인덱스 기반 수정:
set(index, value)(데이터를 바꾸기만 함, 밀어내기 없음)맨 끝에 추가/삭제:
add(value)/remove(size - 1)데이터의 크기가 변경되는 작업으로 인해 만약 배열 크기 증가 작업이 수행되면 O(N) 이 소요된다.
단점 - O(N)
중간/맨 앞에 삽입/삭제:
add(index, value)/remove(index)해당 인덱스 뒤의 모든 데이터를 한 칸씩 밀거나 당겨야 하기 때문
값으로 검색 하는 탐색 과정:
contains(value)/remove(value)값을 찾기 위해 맨 앞부터 순차적으로 검색해야 하기 때문
ArrayList 의 맨 앞 또는 중간 인덱스에 값을 추가 하는 과정
ArrayList 는 내부적으로 정적 배열을 사용한다고 이해 했기 때문에, 이 과정에서 비용이 많이 든다는 것도 알 수 있다.
맨 앞이나 중간에 값을 추가 하게 되면 추가되는 데이터 만큼 크기가 증가한 배열을 새로 생성한다.
기존 ArrayList 에 저장 되어있는 데이터를 크기가 증가한 새로운 배열에 값을 복사한다.
원하는 인덱스에 새로운 값을 추가한다.
이 과정이 발생하고, 만약 배열의 크기가 크다면 훨씬 더 많은 비용이 발생할 수 밖에 없다.
LinkedList
특징
데이터를 삽입, 삭제 과정에서
ArrayList의 배열 복사 행위가 없고 노드가 가르키고 있는 포인터만 변경하기 때문에 상대적으로 빠른 특징을 갖고 있다.head,tail값을 갖고 있는 노드 객체의 연결 덕분에 스택, 큐 같은 자료구조에 구현체로 사용된다.
성능
데이터를 삽입, 삭제하는 과정이 빠르며 최선(맨앞, 맨뒤 -> head, tail 을 저장하고 있음)은 O(1) 이지만, 탐색 과정 때문에 O(N) 의 시간복잡도를 갖고있다.
ArrayList와 반대로 데이터를 조회하는 과정에서 노드의 순차적 접근으로 포인터가 가르키고 있는 값을 계속 타고 들어가서 조회하기 때문에 O(N) 이 된다.
임의의 데이터를 위치 기반으로 조회할 때 순차적 탐색 과정을 거치기 때문에 ArrayList 보다 성능이 좋지 않다.
LinkedList 와 ArrayList 의 차이는 무엇일까?

이렇게 이론적으로 성능의 차이가 나타나는 두 컬렉션이지만, 조슈아 블로치가 X 에 게시한 글에 따르면 설계는 그러하지만 본인은 LinkedList 를 전혀 사용하지 않는다고 밝혔다.
이러한 성능의 차이는 ms 기준으로 나타내어 표현되는 숫자상 차이가 보이지만 개발자가 체감하기엔 굉장히 내부적으로 최적화가 잘 되어있다고 한다.
많은 영상에서 이 주제를 다루고는 있지만 결론적으로 ArrayList 를 기본적으로 사용하고 정말 특수한 환경이 아니라면 LinkedList 를 사용하지 않는다고 한다.
더이상 사용되지 않는 Vector
Vector 는 동기화를 지원하기 때문에 성능이 굉장히 느린 컬렉션으로 실무에서는 적합하지 않아 사용되지 않는다.
자바에서는 Legacy 로 등록 해두었으며, 구 버전과 호환을 위해 남겨둔 상황이다.
Queue
PriorityQueue
특징
Comparable인터페이스를 필수로 구현하여 반환 값에 의해 우선순위를 결정한다.null 을 허용하지 않는다.
동시성을 지원하지 않는다.
heap 자료구조를 사용하여 최대힙 또는 최소힙으로 정렬한다.
성능
Heap의 정렬 기준 값에 따라 루트 노드에 배치되는 특성으로 인해 우선순위가 높은 데이터에 접근할 때 O(1) 로 처리된다.삽입, 루트 노드에 있는 값 삭제는 데이터 처리 후 힙 재정렬 과정으로 인해 O(log N) 으로 처리된다.
특정 값 삭제, 특정 값 조회는 전체를 탐색하기 때문에 O(N) 이 소요된다.
Set
Set 은 비선형 자료구조의 인터페이스로 높은 검색 성능이 필요할 때 주로 선택되는 자료구조이다.
HashSet
특징
hashCode()로 반환되는 해시 함수의 결과 값을 통해 저장될 배열의 인덱스를 결정한다.equals()를 통해 값이 중복 되는지 결정한다.null을 허용한다.동시성을 지원하지 않는다.
성능
O(1) 시간복잡도로 단일 데이터의 삽입, 삭제, 조회할 수 있다.
LinkedHashSet
특징
HashSet을 상속 받고 있기 때문에 데이터 조회에는 빠른 성능을 나타낸다.순서를 보장하기 위해
LinkedHashMap을 내부적으로 사용하며,Entry의before,after를 기억하고 있어 순서를 보장한다.동시성을 지원하지 않는다.
성능
조회, 삽입, 삭제는 HashSet 을 상속 받은 덕분에 O(1) 로 처리한다.
TreeSet
특징
다른
Set자료형과 달리null을 허용하지 않는다.Comparator를 활용하여 사용자가 직접 정의한 정렬 방식을 지원한다.동시성을 지원하지 않는다.
성능
데이터 조회, 삽입, 삭제에 O(log N) 이 소요된다.
Map
Map 인터페이스는 Key 와 Value 를 이용하고, Key 는 항상 고유해야한다.
HashMap
특징
hashCode()의 결과 값을 통해 저장하게 될 배열의 인덱스를 결정하게 된다.equals()를 통해 중복 값을 검증하고, 중복이 발생했을 때LinkedList와RedBlackTree를 사용하며 빠른 검색 성능을 위해 최적화한다.입력 순서를 보장하지 않는다.
Key와Value모두 다null을 허용한다.동시성을 지원하지 않는다.
synchronizedMap 과 ConcurrentHashMap 의 차이
두 방식 모두 멀티 스레드 환경에서 동시성을 보장받기 위해 사용하는 방식이다.
synchronizedMap: Collections 의 메서드로 인자로 받는 컬렉션을 래핑하여 해당 객체에 접근하기 전 락을 먼저 획득하는 객체 수준 잠금을 통해 동시성을 지원한다.
즉, 내부적으로 모든 접근에 대해 동기화를 사용하여 모든 쓰기 작업과 대부분의 읽기 작업에서 전체 맵 잠금이 발생하여 성능 저하가 발생
ConcurrentHashMap: 객체 접근 할 때 락을 걸고 회수하는 방식이 아닌, Hashtable 이 갖고 있는 버킷에 대한 Lock 을 지원하는 더 하위 수준의 잠금을 통해 더 많은 스레드가 병렬로 작업할 수 있음
읽기 작업은 대부분 잠금을 사용하지 않거나 최소한의 잠금을 사용하여 높은 효율성 제공
성능
데이터 조회, 삽입, 삭제 모두 O(1) 을 자랑한다.
해시 충돌로 인해 같은 버킷에 중복된 데이터가 쌓이게 될 경우 O(N) 이 발생하는 것을 방지하기 위해 다양한 컬렉션을 활용하여 O(log N) 으로 성능을 최적화한다.
LinkedHashMap
특징
LinkedHashSet컬렉션의 내부적으로 사용되는 자료구조로, 입력되는 순서를 보장한다.그 외엔
HashMap과 모든 기능이 동일하다.
성능
데이터 조회, 삭제 모두 O(1) 로 처리된다.
참고 자료
Last updated