데이터의 집합(컬렉션)을 다룰 때 배열을 사용하면 중간에 끼워넣기에도 불편하고 크기를 늘릴 수도 없어 여러 가지로 불편한 점이 많다. 그래서 더 편리한 자료구조를 만든 것이 컬렉션이다. 컬렉션과 컬렉션 프레임워크는 혼용해서 많이 쓰기 때문에 용어에 집착할 필요는 없다.
컬렉션 프레임워크는 우리가 자료구조 시간에 배운 그 자료구조들을 쉽게 이용할 수 있도록 제공해준다.
JDK 1.2까지는 제네릭 타입이 없었는데 JDK 5부터는 제네릭을 지원하므로 더 안전하게 사용 가능하다. (JDK 1.5를 JDK 5라고도 함)
컬렉션은 데이터의 집합을 말하는데 특징에 따라 두 가지 종류로 나눌 수 있다.
하나는 동일한 데이터의 집합인 Collection
또 하나는 두 가지 종류(key, value)의 데이터로 이루어진 Map이다.
이렇게 Map은 Collection과 특징이 좀 다르지만 일반적으로 넓은 의미의 컬렉션인 Collection+Map의 의미로 많이 쓴다.
Collection은 인터페이스이다. Collection 인터페이스의 자식 인터페이스로 List, Queue, Set이 있다.
일단 List, Queue, Set의 간단한 특징부터 말하자면 List는 선형적인 자료구조, Queue는 선입선출 자료구조, Set은 순서가 없고 중복이 없는 자료구조이다.
이렇게 List, Queue, Set은 Collection의 자식 인터페이스이고 Map은 Collection의 자식 인터페이스가 아니다.
Map은 key와 value를 하나의 쌍으로 저장하는 자료구조로, key 값은 중복될 수 없다. 따라서 key 값은 유일하다.
이 인터페이스 5개 (Collection, List, Queue, Set, Map)는 꼭 알아두자.
일단 컬렉션 인터페이스와 클래스의 구조부터 살펴보자. 분홍색은 인터페이스, 하늘색은 클래스이다.
List의 구현 클래스 중에서 가장 자주 쓰이는 것은 ArrayList이다. Vector는 ArrayList와 동일한데 동기화를 지원한다. 따라서 멀티스레딩, 다중화에서 사용한다. 멀티스레딩을 하는게 아니라면 굳이 더 느린 Vector를 쓸 필요는 없다.
List의 구현 클래스들을 보다보면 특이한 것을 발견했을 것이다. LinkedList는 List의 구현 클래스이자 Queue의 구현 클래스이다. 클래스를 다중 상속하는 것은 안 되지만 여러 인터페이스를 구현하는 것은 가능하다. LinkedList는 List 인터페이스도 구현하고 Queue 인터페이스도 구현한 것이다. LinkedList의 자식 클래스로 Stack이 있다.
Set의 구현 클래스로는 HashSet과 TreeMap이 있는데 TreeMap은 Map이 아니라 Set이라는 거 기억하자. 헷갈리기 쉽다.
Map에는 HashMap을 자주 사용한다. Hashtable도 종종 쓰는데 t가 소문자라는 거 기억하자. (교수님께서는 아마 만든 사람이 실수한 것 같다고 하셨다)
이제 정말 메소드들을 배워보자.
다음은 Collection 인터페이스가 제공하는 메소드이다.
boolean add(E e) | 객체 e를 컬렉션에 추가한다. |
void clear | 컬렉션 객체에 있는 모든 객체들을 삭제한다. |
boolean contains(Object o) | 객체 o가 컬렉션에 있는지 없는지 반환한다. |
boolean isEmpty() | 컬렉션이 비어있는지 아닌지 반환한다. |
boolean remove(Object o) | 객체 o를 제거하고 제거했는지 아닌지 반환한다. |
int size() | 컬렉션에 있는 객체의 개수를 반환한다. |
T[] toArray(T[] a) | 컬렉션을 배열로 만들어서 반환한다. |
헷갈리지 말아야 할 것은 컬렉션 객체의 크기(길이)를 알고 싶다면 length()가 아니라 size()라는 것이다.
문자열은 length(), 배열은 length, 컬렉션은 size()라는 거 잊지 말자.
Collection이 최상위 인터페이스이므로 이 메소드들은 하위 인터페이스, 클래스도 모두 가지고 있는 공통의 메소드이다.
굳이 컬렉션을 더 불편한 배열로 바꿀 일은 거의 없지만 그래도 배열로 바꾸는 방법은 알아야 하므로 toArray 메소드는 알아두자.
다음은 Map 인터페이스가 제공하는 메소드이다.
void clear() | 맵에 있는 모든 엔트리를 삭제한다. |
boolean containsKey(Object key) | key라는 키를 가진 엔트리가 있는지 없는지를 반환한다. |
boolean containsValue(Object value) | value라는 값을 가진 엔트리가 있는지 없는지 반환한다. |
Set<Map.Entry<K, V>> entrySet() | 맵에 있는 모든 엔트리를 원소로 하는 Set 객체를 만든다. |
V get(Object key) | key에 해당하는 value를 반환한다. |
boolean isEmpty() | 맵 객체가 비어있는지 아닌지를 반환한다. |
Set<K> keySet() | key값들을 모아 Set 객체를 만든다. |
V put(K key, V value) | key-value 엔트리를 맵 객체에 추가한다. |
V remove(Object key) | key에 해당하는 엔트리를 삭제한다. |
int size() | 맵 객체에 저장된 엔트리의 개수를 반환한다. |
Collection<V> values() | 맵에 저장된 value들을 모아 컬렉션 객체를 만든다. |
Map 인터페이스는 키와 값을 쌍으로 저장한다. 메소드만 봐도 containsKey(), containsValue()라는 메소드가 있다.
Map에 저장된 객체 중에 해당 key 또는 value를 가지고 있는지를 알아낼 수 있다.
get 메소드는 key를 주면 value가 나오는 메소드이다. key가 유일하기 때문에 key로 접근해서 value를 얻어올 수 있다.
isEmpty()는 굳이 외우지 않아도 당연히 알테고 put 메소드는 Collection으로 따지면 add와 같은 메소드이다. Map 객체가 <String, Integer> 타입이고 맵 객체 이름이 map이라면 map.put("one", 1); 이렇게 하면 one을 key로, 1을 value로 하는 하나의 쌍이 Map에 추가된다.
여기에서 keySet()과 values() 메소드는 꼭 알아둬야 한다. Map에 있는 데이터들을 Collection 타입으로 바꾸어야 하는 상황이 있을 수도 있다. 그런데key와 value로 이루어져있는데 Map에는 데이터 한 개에 2개의 값이 들어있으므로 key와 value를 따로 따로 가져와야 한다. key들을 가져와서 Collection으로 만드는 게 keySet() 메소드이고 value들을 가져와서 Collection으로 만드는 것은 values() 메소드이다.
그런데 key들을 모아서 컬렉션으로 만드는데 keySet() 메소드의 반환타입은 왜 하필 Set일까?
Map의 특징 중 하나, key값들끼리는 중복이 없으므로 Set의 특징인 중복이 없는 것과 일치한다. 따라서 key값들을 모으면 저장할 자료구조는 Set이 딱이다. keySet() 메소드의 반환타입은 Set<K>인데 K는 key를 말한다. Map 원소들 중 key의 타입이었던 그 타입이 Set의 원소들의 타입이 되는 것이다.
반면 values() 메소드는 반환 타입이 Collection<V>이다. value 값들을 모아서 컬렉션으로 만드는데 value는 중복되어도 되기 때문에 중복 데이터가 있을 수 있다. 따라서 Set으로는 못 바꾸고 Collection으로 바꾸는 것이다. 물론 Set도 Collection이다. (Collection의 자식인터페이스)
사실 여기까지 봤을 때에는 무슨 소리인지 잘 모를 수도 있지만 이에 해당하는 예제 코드들을 보면 이해가 갈 것이다. 예제는 다음 글에 이어가겠다.
'Java' 카테고리의 다른 글
인텔리제이(IntelliJ IDEA) 단축키 및 약어 (0) | 2020.02.06 |
---|---|
[Java] 날짜 문자열, Date, LocalDate 타입 변환, 문자열에서 요일 알아내기 (0) | 2020.02.03 |
[Java] 입출력 스트림 (0) | 2020.01.29 |
[Java] Stream (스트림) (0) | 2020.01.21 |
[Java] Comparator 인터페이스 (0) | 2020.01.21 |