✅ 1. 컬렉션(Collection)이란?
- Collection 자체는 영어로 "수집된 것, 모음"을 의미하며, 프로그래밍 전반에서 데이터의 모음을 표현할 때 자주 쓰인다.
- 프로그래밍에선 여러 원소를 하나의 그룹으로 묶어 관리하기 위한 자료구조를 의미한다.
- 즉, 데이터를 효율적으로 저장하고, 관리(검색, 정렬, 삭제 등) 할 수 있는 자료구조들의 집합을 의미한다.
- 자바에선 JCF(Java Collections Framework)라고 부르며, 인터페이스와 클래스로 해당 자료구조를 제공한다.
- 자바 컬렉션의 주요 인터페이스로는 Collection, List, Set, Queue, Map 이 있다
✅ 2. JCF(Java Collections Framework) 구조

(1) JCF(Java Collections Framework) 인터페이스
Set: 데이터의 순서가 없고, 중복 데이터을 허용하지 않는 자료구조 ( 참고: 내부적으로 Map을 이용함 )
List: 데이터의 중복을 허용하고 순서가 있는 자료구조
Queue: List와 유사하나 원소의 삽입/삭제가 FIFO 방식
Map: 원소가 <key, value>의 형태이며 순서가 없으며, 키값은 중복이 되지않지만 value는 중복이 될 수 있음.
(2) Map 자료구조가 따로 있는데 컬렉션에 포함되는 이유
- Collection은 데이터를 하나씩 저장하는 구조이다. ex) add(E e) / remove(E e)
- Ma은 키-값 쌍(key-value)을 저장하는 구조이다. ex) put(K, V), get(K) 등
- 즉, 구조적으로 의미가 다르기 때문에 인터페이스를 공하지 않아, Collection과 떨어져 있는 구조이다.
- 자바 컬렉션 프레임워크는 자료 구조를 통합된 방식으로 다루기 위한 전체 체계를 의미함.
- 즉, Collection 인터페이스만을 의미하는것이 아닌 Map 또한 자바 컬렉션 프레임워크라고 볼 수 있음.
- 자료를 저장하고 처리하는 목적은 동일하기 때문에 프레임워크에 속하게 된다는 의미이다.
- 공식문서에서는 자료 구조를 통합된 방식으로 다루기 위한 전체 체계라고 말을 하고 있음.
✅ 3. Iterable & Iterator & 향상된 for문
- JCF 프레임워크 구조를 보면 제일 최상위 클래스로 Iterable이 있음.
- Iterable이 최상위인 이유는 밑에서 설명함.
(1) Iterable
public interface Iterable<T> {
Iterator<T> iterator();
}
- 컬렉션 프레임워크의 제일 최상위 인터페이스로, 향상된 for문을 가능하게 해주는 인터페이스이다.
- 즉, 컬렉션 최상위인 이유는 그냥 단순히 향상된 for문을 지원해주기 위해서 있는것임.
(2) Iterator
public interface Iterator<E> {
boolean hasNext(); // 다음 요소가 있는지 확인
E next(); // 다음 요소 반환
void remove(); // (선택적) 현재 요소 삭제
}
- 실제로 요소를 하나씩 꺼내는 기능을 제공해주는 인터페이스이다.
- 실제로 향상된 for문은 해당 인터페이스의 메서드를 통해 컴파일 이후의 for문을 자동으로 만들어준다.
(3) Iterable & Iterator 나눈 이유
- SOLID의 단일 책임 원칙(SRP)을 지키기 위해 인터페이스를 둘로 나눈 것이다.
- Iterable은 Iterator를 만들어주는 공장으로 볼 수 있다
- 해당 Iterable을 반드시 구현하고 Iterator를 구현하여 Iterable을 통해 Iterator를 사용함을 강제하고 있음.
- 향상된 for문은 컴파일 과정에서 iterator() 메서드가 없으면 동작하지 않는다.
- 결국은 둘은 for문 래퍼클래스로 볼 수 있다. for문을 클래스와 메서드로 만들어서 객체로써 제공을 하는 개념임.
(4) 향상된 for문
import java.util.Iterator;
public class MyCollection implements Iterable<String> {
private String[] data = {"apple", "banana", "cherry"};
@Override
public Iterator<String> iterator() {
return new MyIterator();
}
private class MyIterator implements Iterator<String> {
private int index = 0;
@Override
public boolean hasNext() {
return index < data.length;
}
@Override
public String next() {
return data[index++];
}
}
public static void main(String[] args) {
MyCollection collection = new MyCollection();
// 향상된 for문 사용 가능
for (String item : collection) {
System.out.println(item);
}
}
}
- 기본 배열과 Iterable을 구현한 클래스에서만 사용이 가능한 for문이다.
- 해당 코드에서 Iterable을 구현하면 내부의 iterator() 추상 메서드를 구현하게 된다.
- 해당 추상 메서드는 반환타입으로 Iterator를 반환하고 있어, Iterator를 구현을 해서 구현 클래스를 넣어줘야 한다.
- 기본 배열은 JVM이 내부적으로 자동으로 처리를 해주며, Iterable은 직접 구현을 해줘야함.
- Iterable 내부의 iterator() 메서드를 통해 컴파일러가 자동으로 for문을 만들어주기 때문에 인터페이스 구현 필수임.
(5) 향상된 for문 컴파일 변환
import java.util.Iterator;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> list = List.of("a", "b", "c");
// 컴파일러가 변환하는 구조
for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
String item = iterator.next();
System.out.println(item);
}
}
}
- 향상된 for문은 실제로는 존재하지 않는 논리적인 코드이며, 컴파일이 되면 기본 for문으로 바뀌는 구조이다.
- 증감식이 생략된 for문으로 형태를 만들어서 next() 메서드를 통해 증감이 되는 방식으로 만들어진다.
(6) 결론: 향상된 for문은 Iterable / Iterator 필수
public class Main {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("one"); list.add("two"); list.add("three");
list.add("four"); list.add("five");
// 방법1 : for문을 이용하기
for(Iterator<String> it = list.iterator(); it.hasNext();) {
String item = it.next();
System.out.println(item);
}
// 방법2 : while 이용하기
Iterator<String> it = list.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
}
- 결론은 향상된 for문이 컴파일 되면 해당 for문 형태로 변경이 됨. 즉, 개발자 친화적으로 코드가 만들어져 있음.
✅ 4. JCF의 인터페이스 및 클래스

- java.util 패키지에 포함되며 제네릭 타입으로 만들어져 있는 클래스들이다.
- 인터페이스는 Set, List, Queue, Map이 있으며, 구현 클래스는 다양하게 있음.
- 대표적인 구현 클래스는 HashSet, HashMap, ArrayList, LinkedList, TreeSet, TreeMap 등이 있음.
(1) 컬렉션 객체의 선언
Set<Integer> set = new HashSet<>();
List<Integer> list = new ArrayList<>();
List<Integer> list = new LinkedList<>();
Queue<Integer> queue = new LinkedList<>();
Map<String, Integer> map = new HashMap<>();
- 선언은 대표적으로 다형성을 활용해 인터페이스 타입으로 만들고, 구현체를 원하는 구현체에 맞게 넣는다.
- 이유는, 유지보수성, 유연성을 통한 확장성 등 때문에 해당 선언 방식을 이용함.
(2) Collection<E> 인터페이스

- Set, List, Queue 에서 공통으로 지원해야 하는 기능을 정의해둔 인터페이스이다.
✅ 5. HashSet, ArrayList, LinkedList, HashMap 클래스
(1) HashSet<E>
public class Main {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("apple");
set.add("apple");
set.add("banana");
System.out.println(set); // [banana, apple] 또는 [apple, banana] 등 (순서 X, 중복 제거됨)
// set.remove("apple"); 특정 요소 지우기
// set.clear(); 해당 배열 전체 비우기
}
}
- 중복 데이터 저장을 허용하지 않는다. 즉, 중복으로 저장된 값이 있다면 중복 저장된 값은 무시하고 하나만 뽑음.
- 데이터 저장 순서에 따라 순차적으로 저장하지 않고, 불규칙적으로 저장이 됨. 즉, 순서 보장이 되지 않음.
- 내부적으로 HashMap을 이용해서 저장하고 있음. Map의 키값은 중복 저장이 안되며, 순서 보장 또한 안되는데 맵의 키를 이용해서 저장하고 있기 때문에 Map의 특성인 해시테이블 형태로 값이 저장이 된다. ( 걍 Map을 쓰고 있음 )
- 방식은 Map의 key값에 데이터를 넣고, value에는 더미데이터를 넣는 방식을 이용하고 있음.
- 즉, set은 map의 키값만 이용한 자료구조라고 생각하면 됨.
(2) ArrayList<E>
// 장점 - 조회시 매우 빠름
ArrayList<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.add("Cherry");
System.out.println("Second item: " + list.get(1)); // O(1) - 매우 빠름
// 단점 - 중간 삽입/삭제 시 느림 ---------
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
list.add("Item " + i);
}
list.add(50000, "Inserted Item"); // O(n) - 리스트 중간에 삽입 -> 많은 요소가 뒤로 밀려남
- List 인터페이스를 구현한 클래스이다.
- 중복 데이터 저장을 허용하며, 저장 된 순서에 맞게 순차적으로 저장하기 때문에 반복문을 돌리면 순서대로 나옴
- 검색에는 빠르지만, 중간에 삽입과 삭제에는 느리다는 단점이 있음.
- 이유는, 배열의 크기가 고정이 되어있어서, 인덱스로 빠르게 접근이 가능해 검색에는 빠름
- 하지만, 중간에 삽입하는 과정에서 삽입 위치 뒤의 모든 요소를 하나씩 오른쪽으로 밀어야 하기 때문에 느림.
- 삽입과 삭제가 잦은 경우에는 LinkedList가 더 유리함. 대신 접근은 느림.
(3) LinkedList<E> - List & Queue 구현 클래스
LinkedList<String> list = new LinkedList<>();
// 요소 추가
list.add("Apple");
list.add("Banana");
list.add("Cherry");
list.addFirst("Mango"); // 맨 앞에 요소 추가
list.addLast("Orange"); // 맨 뒤에 요소 추가
System.out.println("LinkedList 내용: " + list); // 요소 출력
System.out.println("첫 번째 요소: " + list.getFirst()); // 첫 번째 요소 가져오기
System.out.println("마지막 요소: " + list.getLast()); // 마지막 요소 가져오기
list.remove("Banana"); // 특정 요소 삭제
System.out.println("Banana 제거 후: " + list);
// 리스트 순회
System.out.println("리스트 순회:");
for (String fruit : list) {
System.out.println(fruit);
}
- 데이터와 다음 데이터의 위치(참조)를 함께 저장하는 노드들이 연결된 자료구조를 의미한다.
- 각 노드는 데이터, 다음 노드를 가리키는 포인터를 가지고 있는다.
- 노드들이 화살표(링크)로 연결되어 있어서, 한 노드에서 다음 노드로 이동하는 방식이다.
- 배열과 다르게 메모리 상에 연속적으로 있지 않아도 되어 동적 할당이 가능함.
- 장점: 배열과 달리 중간에 데이터를 넣거나 빼려면 요소를 이동시키지 않아도 되서 삽입과 삭제가 빠름
- 단점: 배열은 인덱스로 바로 접근이 가능하지만, 링크드 리스트는 처음부터 차례로 노드를 따라가야 함. 임의 접근 느림.
- Queue 인터페이스 또한 같이 구현하고 있는 클래스이다. 즉, queue 메서드를 이용해서 queue로 사용이 가능함.
(4) HashMap<K, V>
Map<String, Integer> map = new HashMap<>(); // key, value 로 저장
// 삽입
map.put("apple", 100);
map.put("banana", 200);
// 조회
int applePrice = map.get("apple");
System.out.println("apple의 가격은 " + applePrice + "원입니다.");
- Map<K, V> 인터페이스를 구현한 클래스이며, key, value로 이루어진 원소로 데이터를 저장함.
- key는 중복이 불가능하며, 하나의 key에 하나의 value만 넣을 수 있음.
- 해당 key, value 여러 쌍들의 순서를 보장하지 않음.
- 해싱을 이용하여 Map 인터페이스를 구현한 클래스이다.
- 자료 탐색 방법이 ArrayList, LinkedList 와는 전혀 다름. 즉, Collection과 떨어져 있음.
'공부 > ☕Java' 카테고리의 다른 글
| [Java] 멀티 스레드 프로그래밍 (2) | 2025.06.04 |
|---|---|
| [Java] 자바 스트림(Stream) 개념 + 예제 총정리 📚 (2) | 2025.06.03 |
| [Java] java.nio 사용법 | Path, Files, FileChannel 예제 모음 (3) | 2025.05.30 |
| [Java] java.io 패키지 입출력 스트림 쉽게 이해하기 (2) | 2025.05.28 |
| [Java] java.lang 패키지 정리: Object, String, Math 클래스 총정리 (0) | 2025.05.26 |