이전까지는 String, StringBuffer, StringBuilder를 읽어봤고, 원래 그 다음은 내용은 Math 클래스와 BigInteger, BigDecimal, 포맷팅, 날짜형식 등이 있었는데, 솔직히 좀 속성으로 자바에 대해 알아보고 싶기도하고 마침 11장이 핵심기능인 컬렉션이기도해서 9장에서 11장으로 넘어왔다. 9장 나머지부분과 10장은 시간날 때 읽어보기로
컬렉션 프레임웍
JAVA API에서든 컬렉션을 "데이터 군(group)을 다루고 표현하기 위한 단일화된 구조"라고 정의한다. 배열이나 리스트 같은 구조를 생각하면 된다.
JDK1.2까지는 다수의 데이터를 저장할 수 있는 Vector, HashTable, Properties 등의 클래스만 존재했고, 이 클래스들의 상위클래스는 Object 클래스였다.
JDK1.2에 컬렉션 프레임웍이 등장하면서 Set, List, HashMap 등의 클래스들이 추가되고 모든 컬렉션 클래스를 표준화된 방식으로 다룰 수 있게되었다. Vector 클래스를 예를 들면 JDK1.1까지는 Object 클래스만 상속했다면 현재는 AbstractList를 상속하며, List<>를 구현하는 등 컬렉션 프레임웍의 체계 안으로 들어와있는 상태다.
JDK1.2는 1998년에 나왔다고한다. 그 시절의 자바는 어떤 언어였던가ㄷㄷ...
배열 쓰면 되기 한다만 배열은 좀 불편해
컬렉션의 상속체계

대략적으로 이런 상속구조를 가지고있다.
컬렉션 프레임웍은 배열과 같은 컬렉션데이터들의 경우를 세 가지로 구분하고 위와 같은 상속체계를 구축하고 List, Set, Map을 정의하고 List와 Set의 공통기능을 추려 Collection 인터페이스를 정의하였다. (Iterable 인터페이스는 JDK5에서 등장했다.)
뒤에 나오지만 Map 인터페이스는 List와 Set과는 사뭇 달라서 그 어디에도 들어가지 않았다. 현재도 Map 인터페이스는 그 어느것도 상속하지 않고(Object 제외) 확장하지 않는다.
대략적인 기능은
List - 중복을 허용하며 순서가 있는 데이터의 집합
Set - 중복을 허용하지 않으며 순서가 없는 데이터의 집합
Map - key-value 형태의 데이터의 집합. 순서가 없고, key는 중복을 허용하지 않는다.
모든 컬렉션 프레임웍은 저 세 인터페이스 중 하나를 상속, 구현하고 있으며, 세 컬렉션 프레임웍의 차이와 특징 꽤 명확하기 때문에 적재적소에 잘 활용하기만하면 된다. 명명법도 ArrayList, HashSet, HashMap 등 어느 인터페이스를 구현하는지 알기 쉽게 되어있다. (다만 컬렉션 등장 이전부터 존재했던 클래스들은 예외)
Collection 인터페이스의 메서드
Collection의 인터페이스는 Set과 List에서 공통으로 사용될 수 있도록 정의된 것이다.
public interface Collection<E> extends Iterable<E> {
//컬렉션에 저장된 객체의 수 반환
int size();
//컬렉션이 비어있는지 확인
boolean isEmpty();
//객체가 컬렉션에 포함되는지 확인
boolean contains(Object o);
boolean containsAll(Collection<?> c);
//컬렉션의 이터레이터를 얻어옴 (무슨말이지?)
Iterator<E> iterator();
//컬렉션을 Object 배열로 반환
Object[] toArray();
//컬렉션을 지정된 객체배열로 반환
<T> T[] toArray(T[] a);
//객체 또는 컬렉션의 객체들을 컬렉션에 추가
boolean add(E e);
boolean addAll(Collection<? extends E> c);
//지정된 객체 또는 컬렉션을 제거
boolean remove(Object o);
boolean removeAll(Collection<?> c);
//지정된 컬렉션에 포함된 객체를 제외하고
//다른 모든 객체들은 제거
//컬렉션이 변화하면 true, 아니면 false
boolean retainAll(Collection<?> c);
//컬렉션의 모든객체 삭제
void clear();
}
default 메서드와 Object 상속메서드는 제외했다.
컬렉션의 하위클래스에 속하는 클래스들은 모두 이 기능을 사용할 수 있다.
List
리스트는 중복을 허용하며 순서가 있는 컬렉션이다.

대표적인 리스트의 구현체들은 Vector, ArrayList, LinkedList가 있다.
ArrayList는 가변크기의 배열이다. 컬렉션을 다루다보면 한 번씩은 쓰게된다.
Vector 클래스는 ArrayList와 동일한 기능을 제공한다. 컬렉션과 ArrayList가 등장하기 전 가변크기의 배열을 지원하기 위해 사용되었으나 현재는 기능이 완벽히 동일하지만 성능이 더 좋은 ArrayList에 밀려 잘 사용되지 않는다.
단 스레드안전을 보장해야하는 경우에는 Vector를 사용하는 것이 좋다. Vector는 synchronized 키워드를 통해 한 번에 한 스레드만 접근할 수 있으나 ArrayList는 그렇지 않다.
그런데 Vector의 동기화처리는 그렇게 철저히 동기화처리된 편도 아니고 동기화 때문에 성능이 저하되기 때문에 보통 synchronizedList를 주고 사용하는 경우도 있다.
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
/**
* Pointer to first node.
*/
transient Node<E> first;
/**
* Pointer to last node.
*/
transient Node<E> last;
...
}
LinkedList는 흔히 생각하는 배열처럼 하나의 하나의 배열이 있으면 배열 내 어느공간에 값을 저장하는 것이 아니라 C에서 포인터로 배열을 구현하는 것처럼 클래스에 이전 노드를 가리키는 포인터와 다음 노드를 가리키는 포인터가 있다.
여기서 노드란 객체 하나를 가리킨다.
그래서 LinkedList는 각자의 객체(노드)들이 포인터를 통해 연결된 자료구조라고 보면 될 듯하다.
🧱 자바 LinkedList 구조 & 사용법 - 정복하기
LinkedList 컬렉션 자바의 Linked List는 ArrayList와 같이 인덱스로 접근하여 조회 / 삽입이 가능하지만 내부 구조는 완전히 다르게 구성되어 있다는 점이 특징이다. ArrayList는 내부적으로 배열을 이용하
inpa.tistory.com
이 글을 참고하면 더 자세하게 알 수 있을듯하다.
void add(int index, E element); //지정된 인덱스에 요소 삽입
boolean addAll(int index, Collection<? extends E> c); //지정된 위치에 컬렉션 삽입
E get(int index); //지정된 위치에 있는 객체 반환
int indexOf(Object o); //객체의 위치반환 (첫번째부터 순방향으로 조사)
int lastIndexOf(Object o); //객체의 위치반환 (마지막부터 역방향 조사)
//ListIterator를 반환함
ListIterator<E> listIterator();
ListIterator<E> listIterator(int index);
E remove(int index); //지정위치의 객체 삭제, 그 객체를 반환함
E set(int index, E element); //지정된 위치에 객체를 저장
default void sort(Comparator<? super E> c) { ... } //Comparator 기준으로 요소정렬
List<E> subList(int fromIndex, int toIndex); //특정범위에 있는 객체를 반환
List 인터페이스에서 재정의되거나 추가된 메서드들
대체적으로 컬렉션에서 상속받은 메서드를 재정의하거나 새로 메서드를 만들어내는데, 리스트가 순서가 있는 메서드다보니 주로 인덱싱을 통해 리스트의 요소들에 접근할 수도록 메서드가 재정의되고 있다.
Set
Set은 중복을 허용하지 않고, 순서가 없는 자료구조다.

대표적인 Set의 구현체로는 SortedSet, HashSet이 있다.
책에서 설명은 이걸로 끝났다.
리스트가 인덱싱 기능을 추가해야하는 것과 달리 Set은 인덱싱 기능도 필요없고, 중복만 허용하지 않으면 되므로 컬렉션에서 받은 메서드를 어떻게 잘 재정의하는지가 중요하기 때문인 듯하다.
HashSet은 어차피 Set은 순서가 없기 때문에 아무 순서로나 값을 저장하게되며, 저장순서도 보장하지 않는다. (보장하려면 LinkedHashSet을 써야한다.) Set에서 어느 요소를 불러올 경우 전체 HashSet을 순회하며 요소를 찾는다.
반면 SortedSet은 오름차순으로 값을 정렬한다.
SortedSet의 구현체인 TreeSet을 예시로들면, TreeSet은 이진탐색트리에 따라 값을 오름차순으로 저장하게된다.
그래서 값을 추가하거나 삭제하는데에는 시간이 조금 더 걸리지만 정렬과 검색에는 높은 성능을 보인다.
여기서 값의 비교는 Comparable 인터페이스의 구현체인 경우 compareTo 메서드를 사용하게 정렬한다. 그렇지 않을 경우 정렬되지 않는다.
Map
Map은 key-value 형태로 저장되는 컬렉션 클래스를 구현하는데 사용된다.

대표적인 구현체로는 Hashtable, HashMap, SortedMap 등이 있다.
//Map의 key-value 쌍의 개수 반환
int size();
boolean isEmpty();
//key/value객체와 일치하는 key/value 객체가 있는지 확인
boolean containsKey(Object key);
boolean containsValue(Object value);
//key를 통해 value값을 가져옴
V get(Object key);
//key-value 쌍을 Map에 추가
V put(K key, V value);
void putAll(Map<? extends K, ? extends V> m);
//key값을 기준으로 제거
V remove(Object key);
//모든 key-value쌍을 제거
void clear();
//Map에 저장된 모든 key를 반환
//key는 중복허용이 되지 않아 Set으로 반환
Set<K> keySet();
//Map에 저장된 모든 value를 반환
Collection<V> values();
//Map의 key-value 쌍을 Map.Entry타입의 객체로 저장한 Set으로 반환
Set<Map.Entry<K, V>> entrySet();
Map에 정의된 메서드. (default와 static 제외)
Map.Entry
Entry는 Map 인터페이스 내부에 구현된 내부인터페이스다. Map에 저장되는 key-value 쌍을 표현하는데 사용된다.
Map.Entry 는 키와 값이라는 두 가지 요소를 저장하는 튜플 이라고 생각할 수 있습니다.
https://recordsoflife.tistory.com/314#google_vignette
key-value로 표현하는데 사용되며, Map 구현체의 entrySet을 통해 Map.Entry 객체들을 Set에 담아 반환받을 수 있다.
Map<String, String> map = new HashMap<>;
for (String key : map.keySet()) {
System.out.println("key: " + key);
System.out.println("value: " + map.get(key));
}
Map<String, String> map = new HashMap<>;
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println("key: " + entry.getKey();
System.out.println("value: " + entry.getValue());
}
그래서 위의 코드를 아래와같이 변경할 수 있다.
of(), copyof()
JDK9부터 추가된 List, Set, Map을 생성 및 반환하는 메서드다.
List list = List.of(1, 2, 3);
Set set = Set.of(1, 2, 3);
위와 같이 .of 메서드를 사용해 인자로 준 요소들이 들어간 List나 Set을 만들 수 있다.
Map<String,String> map = Map.ofEntries(Map.entry("A","B"));
Map은 .ofEntries() 메서드를 써야하며, 인자로는 entry 객체가 들어간다.
.copyof() 메서드는 매개변수로 컬렉션 프레임웍을 받아 복사 후 반환한다.
List<Integer> list = List.of(1,2,3,4,5);
List<Integer> copy = List.copyOf(list);
이러면 list와 copy 모두 같은 값을 가지게된다.
다만 얕은복사로 복사되기 때문에 같은 객체를 가리키지는 않는다.
또 중요한 점으로는 of와 copyof 메서드를 통해 반환된 컬렉션은 읽기전용이다.
따라서 값을 변경할 수 없다.
오늘은 이렇게 컬렉션 프레임웍의 정의, 컬렉션 프레임웍의 구현체들이 무엇이 있는지 알아봤는데,
다음번에는 ArrayList, Vector처럼 실제 구현체에 대해서 좀 더 자세히 알아볼 것 같다.
'언어공부 > Java | Kotlin' 카테고리의 다른 글
| [CS] 자바의 정석 독서 #20 - 스택과 큐 in Java (0) | 2025.11.22 |
|---|---|
| [CS] 자바의정석 독서 #19 - ArrayList, Vector, LinkedList (1) | 2025.11.21 |
| [CS] 자바의 정석 독서 #17 - StringBuffer, StringBuilder (0) | 2025.11.19 |
| [CS] 자바의 정석 독서 #16 - String 클래스 파헤치기 (0) | 2025.11.18 |
| [CS] 자바의 정석 독서 #14 - 예외처리 (0) | 2025.11.11 |
