자바 컬렉션 프레임워크

PEPPERMINT100·2022년 11월 22일
0

서론

자바에서 컬렉션 프레임워크에 대해 설명해달라는 질문을 받았다. 실무를 보며 수없이 사용하는 List, Set, Map이 있고 Set은 중복을 허용하지 않는다. List에서 데이터를 찾을 때는 배열의 모든 값을 순회해야 해서 배열의 길이 만큼 시간이 걸리지만 Map에선 키를 통해 바로 값에 접근하기 때문에 빠르다.

실무를 보며 수없이 사용하는 컬렉션 프레임워크지만 이외에는 대답할 수가 없었다. List, Set의 차이도 중복 허용을 제외하면 전혀 알지 못했고 예전에 HashMap이 Array보다 검색이 빠르다는 질문을 받았을 때 처럼 뭔가 당연한데 대답을 못하는 상황을 겪었다.

컬렉션 프레임워크는

자바에서 여러 데이터를 모아놓기 위해서 등장했다. 여러 데이터를 모아놓으려면 배열을 사용하면 된다. 하지만 자바는 배열의 크기를 지정되어 있기 때문에 기존에 지정한 배열보다 더 많은 원소를 넣으려고 하면 예외가 발생한다.

이러한 경우를 예방하기 위해 자바는 여러 데이터 관리를 위해 컬렉션 프레임워크를 제공한다.
컬렉션 프레임워크에는 크게 List, Set, Map이 있다.

위처럼 Map을 제외하면 전부 Collection을 상속받아 구현되어 있으며 데이터요소를 제네릭을 통해 타입을 지정할 수 있도록 되어 있다.

List

List는 순서를 가지고 중복을 허용하는 배열이다. ArrayList, LinkedList, Vector가 List를 상속받아 구현된다.

ArrayList는 객체가 삽입될 때 인덱스를 부여받아 데이터를 저장한다. 

따라서 데이터에 접근할 때는 성능이 좋으나 중간에 요소가 삽입, 삭제가 일어나면 중간에서 뒤에 요소들은 인덱스를 한 칸씩 밀어야 하므로 성능이 좋지 않다.

LinkedList는 데이터와 주소로 이루어진 클래스들을 순서대로 쭉 이어 연결한다. 값을 검색하려면 처음부터 연결된 리스트를 전부 탐색하므로 성능이 떨어지나 노드 삽입, 삭제에서는 노드를 단순히 연결하거나 끊어내기만 하면 되므로 성능이 좋다.

Vector는 기본적으로 ArrayList와 비슷하나 멀티 스레드에서 동기화를 지원한다. 그런데 멀티스레드 환경이 아니고 싱글 스레드 환경에서도 동기화를 한다. 즉 스트링버퍼와 빌더의 차이처럼 단일 스레드에서의 성능에서 유리한 점은 없다.

Set

Set은 순서가 없으며 데이터의 중복을 허용하지 않는다. List처럼 각 데이터의 위치를 알려주는 힌트를 제공하지 않는다. 

Java에서 List에는 get() 메소드가 있어 인덱스의 값에 바로 접근해서 가져오지만 Set에는 이 메소드가 존재하지 않는다.

또 인덱스가 없어 데이터를 순회하기가 어렵지만 Iterator 를 지원하기 때문에 이를 통해 Set을 전부 순회하며 값을 찾을 수 있다.

Set에는 HashSet, LinkedHashSet, TreeSet이 존재한다.

HashSet은 가장 기본적인 Set으로 입력 순서를 보장하지 않고 순서도 보장하지 않는다. 일반적으로 데이터의 중복 여부를 체크하는데 사용되며 hash를 통해 데이터를 빠르게 찾을 수 있다.

LinkedHashSet는 Set이지만 특이하게 데이터의 저장순서를 보장한다. 중복은 허용하지 않으면서 순서의 보장이 필요할 때 사용한다.

TreeSet은 HashSet처럼 순서를 보장하지 않으며 중복도 허용하지 않는다. TreeSet은 이진 트리를 이용해 구현되므로 Set이 정렬되어 있다. 즉 특정 구간의 집합 요소를 탐색할 때 유리하다.

Map

Map은 특이하게 Collection 인터페이스를 상속 받지 않는다. Map은 key, value쌍으로 이루어져 있으며 Key의 집합은 Set으로 이루어져 있다. 

자바에서 Map의 keySet 메소드를 사용하면 Set 객체를 반환하는 것을 확인할 수 있다. 즉 Key의 중복을 허용하지 않으며 같은 키로 Map에 Value를 저장하면 덮어 씌워진다.

Map에는 HashMap과 HashTable, LinkedHashMap, TreeMap이 존재한다. 

HashMap은 가장 대표적인 Map 컬렉션이다. 기본적으로 크기가 정해진 배열처럼 어느정도 메모리를 확보하고 키에 값을 저장하는데, 저장공간 보다 많은 값이 들어오면 저장공간을 거의 두배로 늘린다. 

따라서 Map에 들어오는 값의 개수를 안다면 초기에 지정해주는 것이 좋다. 
또 null을 허용하며 멀티 스레드에서 동기화를 지원하지 않는다.

HashTable은 null을 허용하지 않으며 동기화를 지원한다. 따라서 멀티 스레드 환경에 적합하다.

TreeMap은 TreeSet과 비슷하게 키가 정렬되어 정렬된 범위를 탐색할 때 유리하다.

결론

실무에서도 데이터 집합에 특성에 맞게 컬렉션 프레임워크를 사용하는게 맞는데 거의 대부분에 경우에 깊게 고민하지 않고 ArrayList를 사용해왔다. 

이론과 실무에는 언제든지 거리가 있다고 믿는다. 또 적절하지 않은 컬렉션 프레임워크를 사용했다고 잘 돌아가던 Java 프로그램이 갑자기 죽는 일은 거의 없을 것이라고 생각한다.

하지만 컬렉션 프레임워크의 선택에도 이론을 알고 선택하려는 습관을 갖는 것이 더 좋은 개발자로 가는 길이라는 것에는 100% 확신이 든다.

profile
기억하기 위해 혹은 잊어버리기 위해 글을 씁니다.

0개의 댓글