자바는 자료구조를 사용해 객체들을 효율적으로 추가, 삭제, 검색할 수 있도록 인터페이스와 구현 클래스를 java.util 패키지에서 제공하는데, 이를 총칭해 컬렉션 프레임워크라고 한다.
컬렉션 프레임 워크의 주요 인터페이스로는 List, Set, Map이 있다.

List 컬렉션은 배열과 비슷하게 객체를 인덱스로 관리한다.
배열과의 차이점은 저장 용량 (capacity)이 자동으로 증가하며, 객체를 저장할 때 자동 인덱스가 부여된다는 점이다. 그리고 추가, 삭제, 검색을 위한 다양한 메소드들이 제공된다.
List 컬렉션은 객체 자제를 저장하는 것이 아니라 객체의 번지를 참조한다. 따라서 동일한 객체를 중복 저장할 수 있는데 이 경우 동일한 번지가 참조된다. null도 저장이 가능하며 이 경우 해당 인덱스는 객체를 참조하지 않는다.
| 메소드 | 설명 |
|---|---|
| boolean add(E e) | 주어진 객체를 맨 끝에 추가 |
| void add(int index, E element) | 주어진 인덱스에 객체를 추가 |
| E set(int index, E element) | 주어진 인덱스에 저장된 객체를 주어진 객체로 변경 |
| boolean contains(Object o) | 주어진 객체가 저장되어 있는지 조사 |
| E get(int index) | 주어진 인덱스에 저장된 객체를 리턴 |
| boolean isEmpty() | 컬렉션이 비어있는지 조사 |
| int size() | 저장되어 있는 전체 객체 수를 리턴 |
| void clear() | 저장된 모든 객체를 삭제 |
| E remove(int index) | 주어진 인덱스에 저장된 객체를 삭제 |
| boolean remove(Object o) | 주어진 객체를 삭제 |
List<E> list = new ArrayList<E>();E : 타입 파라미터
List<String> list = new ArrayList<String>(); List<String> list = new ArrayList<>();
ArrayList에 객체를 추가하면 0번 인덱스 부터 차례대로 저장된다. ArrayList에서 특정 인덱스의 객체를 제거하면 바로 뒤 인덱스 부터 마지막 인덱스까지 모두 앞으로 1씩 당겨진다. 마찬가지로 삽입하는 경우에도 해당 인덱스부터 마직막 인덱스까지 모두 1씩 밀려난다.
따라서 저장된 객체 수가 많고 득정 인덱스에 객체를 추가하거나 제거하는 일이 빈번하다면 ArrayList 보다 LinkedList를 사용하는 것이 좋다.
import java.util.*;
public class Test {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("Java");
list.add("JDBC");
list.add("Servlet/JSP");
list.add(2, "Database");
list.add("iBATIS");
int size = list.size();
System.out.println("총 객체 수: " + size);
System.out.println();
String skill = list.get(2);
System.out.println("2: " +skill);
System.out.println();
for(int i=0; i<list.size(); i++) {
String str = list.get(i);
System.out.println(i +":"+str);
}
System.out.println();
list.remove(2);
list.remove(2);
list.remove("iBATIS");
for(int i=0; i<list.size(); i++) {
String str = list.get(i);
System.out.println(i +":"+str);
}
}
}
총 객체 수: 5
2: Database
0:Java
1:JDBC
2:Database
3:Servlet/JSP
4:iBATIS0:Java
1:JDBC
Vector는 ArrayList와 동일한 내부 구조를 가지고 있다.
ArrayList와 다른 점은 Vector는 동기화된 메소드로 구성되어 있기 때문에 멀티 스레드가 동시에 Vector의 메소드들을 실행 할 수 없고, 하나의 스레드가 메소드를 실행을 완료해야만 다른 스레드가 메소드를 실행할 수 있다는 것이다. 그래서 멀티 스레드 환경에서 안전하게 객체를 추가, 삭제할 수 있다. (이것을 스레드 안전 (thread safe) 하다고 표현한다.)
List<E> list = new Vector<E>(); List<E> list = new Vector<>();
import java.util.*;
class Board {
String title, content, writer;
Board(String title, String content, String writer){
this.title = title;
this.content = content;
this.writer = writer;
}
}
public class Test {
public static void main(String[] args) {
List<Board> list = new Vector<Board>();
list.add(new Board("제목1", "내용1", "글쓴이1"));
list.add(new Board("제목2", "내용2", "글쓴이2"));
list.add(new Board("제목3", "내용3", "글쓴이3"));
list.add(new Board("제목4", "내용4", "글쓴이4"));
list.add(new Board("제목5", "내용5", "글쓴이5"));
list.remove(2);
list.remove(3);
for(int i = 0; i<list.size(); i++) {
Board board = list.get(i);
System.out.println(board.title + "\t" + board.content + "\t" + board.writer);
}
}
}
제목1 내용1 글쓴이1
제목2 내용2 글쓴이2
제목4 내용4 글쓴이4
List 구현 클래스이므로 ArrayList와 사용방법은 같지만 내부 구조는 완전히 다르다. ArrayList는 내부 배열에 객체를 저장해서 관리하지만, LinkedList는 인접 참조를 링크해서 체인처럼 관리한다.
LinkedList에서 특정 인덱스의 객체를 제거하면 앞뒤 링크만 변경되고 나머지 링크는 변경되지 않는다. 삽입할 때도 마찬가지이다.
LinkedList가 처음 생성될 때는 어떠한 링크도 만들어지지 않기 때문에 내부는 비어있다고 보면 된다.
List<E> list = new LinkedList<E>(); List<E> list = new LinkedList<>();
끝에서부터 순차적으로 추가/삭제하는 경우는 ArrayList가 빠르지만,
중간에 추가/삭제 하는 경우는 LinkedList가 더 빠르다.
List 컬렉션은 객체의 저장 순서를 유지하지만, Set 컬렉션은 저장 순서가 유지되지 않는다. 객체를 중복해서 저장할 수 없고, 하나의 null만 저장할 수 있다. (수학의 집합과 유사한 개념)
Set 컬렉션에는 HashSet, LinkedHashSet, TreeSet 등이 있다.
| 메소드 | 설명 |
|---|---|
| boolean add(E e) | 주어진 객체를 저장, 성공적으로 저장되면 true, 중복 객체면 false 반환 |
| boolean contains(Object o) | 주어진 객체가 저장되어 있는지 조사 |
| boolean isEmpty() | 컬렉션이 비어있는지 조사 |
| Iterator<E> iterator() | 저장된 객체를 한 번씩 가져오는 반복자를 리턴 |
| int size() | 저장되어 있는 전체 객체 수를 리턴 |
| void clear() | 저장된 모든 객체를 삭제 |
| boolean remove(Object o) | 주어진 객체를 삭제 |
Set 컬렉션은 인덱스로 객체를 검색해서 가져오는 메소드가 없다.
대신 전체 객체를 대상으로 한 번씩 반복해서 가져오는 반복자(Iterator)를 제공한다.
Iterator 인터페이스에 선언되어있는 메소드는 다음과 같다.
| 리턴타입 | 메소드 | 설명 |
|---|---|---|
| boolean | hasNext() | 가져올 객체가 있으면 true, 없으면 false 리턴 |
| E | next() | 컬렉션에서 하나의 객체를 가져온다. |
| void | remove() | Set 컬렉션에서 객체를 제거한다. |
HashSet은 Set 인터페이스의 구현 클래스이다.
Set<E> set = new HashSet<E>();
Set<String> set = new HashSet<String>(); Set<String> set = new HashSet<>();
HashSet은 객체들을 순서 없이 저장하고 동일한 객체는 중복 저장하지 않는다. HashSet이 판단하는 동일객체란 꼭 같은 인스턴스를 뜻하지는 않는다. HashSet은 객체를 저장하기 전에 먼저 객체의 HashCode() 메소드를 호출해서 해시코드를 얻어내고, 이미 저장되어 있는 객체들의 해시코드와 비교한다. 만약 동일한 해시코드가 있다면 다시 equals()메소드로 두 객체를 비교해 true가 나오면 동일한 객체로 판단하고 중복저장을 하지 않는다.
import java.util.*;
public class Test {
public static void main(String[] args) {
Set<String> set = new HashSet<String>();
set.add("Java");
set.add("JDBC");
set.add("Servlet/JSP");
set.add("Java"); // Java 객체는 이미 저장 되어서 저장 안됨
set.add("iBATIS");
int size = set.size();
System.out.println("총 객체 수: " +size);
Iterator<String> iterator = set.iterator();
while(iterator.hasNext()) {
String element = iterator.next();
System.out.println("\t" + element);
}
set.remove("JDBC");
set.remove("iBATIS");
System.out.println("총 객체 수: " + set.size());
iterator = set.iterator();
for(String element : set) {
System.out.println("\t" + element);
}
set.clear();
if(set.isEmpty()) {
System.out.println("비어 있음");
}
}
}
총 객체 수: 4
Java
JDBC
Servlet/JSP
iBATIS
총 객체 수: 2
Java
Servlet/JSP
비어 있음
Map 컬렉션은 Key와 Value로 구성된 Map.Entry 객체를 저장하는 구조를 가지고 있다. Entry는 Map 인터페이스 내부에 선언된 중첩 인터페이스이다. Kye와 Value는 모두 객체이다.
Key는 중복 저장될 수 없지만 Value 값은 중복 저장될 수 있다.
Map 컬렉션에는 HashMap, Hashtable, LinkedHashMap, Properties, TreeMap 등이 있다.
| 메소드 | 설명 |
|---|---|
| V put(K key, V value) | 주어진 키로 값을 저장. 새로운 키일 경우 null 값을 리턴, 동일한 키가 있을 경우 값을 대체하고 이전 값을 리턴 |
| boolean containsKey(Object key) | 주어진 키가 있는지 여부를 확인 |
| boolean containsValue(Object value) | 주어진 값이 있는지 여부를 확인 |
| Set<Map.Entry<K,V>> entrySet() | 키와 갑의 쌍으로 구성된 모든 Map.Entry 객체를 Set에 담아서 리턴 |
| boolean contains(Object o) | 주어진 객체가 저장되어 있는지 조사 |
| V get(Object key) | 주어진 키가 있는 값을 리턴 |
| boolean isEmpty() | 컬렉션이 비어있는지 조사 |
| Set<K> keySet() | 모든 키를 Set 객체에 담아서 리턴 |
| int size() | 저장되어 있는 전체 키의 수를 리턴 |
| Collection<V> values() | 저장된 모든 값을 Collection에 담아 리턴 |
| void clear() | 저장된 모든 Map.Entry를 삭제 |
| V remove(Object key) | 주어진 키와 일치하는 Map.Entry를 삭제하고 값을 리턴 |
HashMap은 Map인터페이스를 구현한 대표적인 Map 컬렉션이다. HashMapdml 키로 사용할 객체는 hashCode()와 equals() 메소드를 재정의해서 동등 객체가 될 조건을 정해야 한다. 객체가 달라도 동등 객체라면 같은 키로 간주하고 중복 저장되지 않도록 하기 위함이다. 동등 객체의 조건은 hashCode()의 리턴값이 같아야 하고, equals() 메소드가 true를 리턴해야한다.
키 타입은 주로 String을 많이 사용하는데 String은 hashCode()와 equals() 메소드가 재정의 되어있다.
Map<K, V> map = new HashMap<K, V>();
Map<String, Integer> map = new HashMap<String, Integer>(); Map<String, Integer> map = new HashMap<>();
키와 값의 타입은 기본 타입(byte, short, int, float, double, boolean, char)을 사용할 수 없고 클래스 및 인터페이스 타입만 사용 가능하다.
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Test {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("미니언", 85);
map.put("홍길동", 90);
map.put("제이크", 80);
map.put("홍길동", 95); //키값이 같아서 마지막에 저장한 값으로 대체됨
System.out.println("총 Entry 수: " + map.size());
System.out.println("\t홍길동 : " +map.get("홍길동"));
System.out.println();
Set<String> keySet = map.keySet();
Iterator<String> keyIterator = keySet.iterator();
while(keyIterator.hasNext()) {
String key = keyIterator.next();
Integer value = map.get(key);
System.out.println("\t" + key + " : " + value);
}
System.out.println();
map.remove("홍길동");
System.out.println("총 Entry 수: " + map.size());
Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
Iterator<Map.Entry<String, Integer>> entryIterator = entrySet.iterator();
while(entryIterator.hasNext()) {
Map.Entry<String, Integer> entry = entryIterator.next();
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println("\t" + key + " : " + value);
}
System.out.println();
map.clear();
System.out.println("총 Entry 수: " + map.size());
}
}
총 Entry 수: 3
홍길동 : 95미니언 : 85
홍길동 : 95
제이크 : 80총 Entry 수: 2
미니언 : 85
제이크 : 80총 Entry 수: 0
Hashtable은 HashMap과 동일한 내부구조를 가지고 있다. Hashtable도 키로 사용할 객체는 HashCode()와 equals() 메소드를 재정의해서 동등객체가 될 조건을 정해야한다.
차이점은 Hashtable은 동기화된 메소드로 구성되어 있기 때문에 멀티 스레드가 동시에 Hashtable의 메소드를 실행할 수 없고, 하나의 스레드가 실행을 완료해야만 다른 스레드를 실행할 수 있다는 것이다.
멀티스레드 환경에서 객체를 안전하게 추가, 삭제할 수 있기 때문에 Hashtable은 스레드에 안전하다.
Map<K, V> map = new Hashtable<K, V>();
Map<String, Integer> map = new Hashtable<String, Integer>(); Map<String, Integer> map = new Hashtable<>();
import java.util.*;
public class Test {
public static void main(String[] args) {
Map<String, String> map = new Hashtable<String, String>();
map.put("spring", "12");
map.put("summer", "123");
map.put("fall", "1234");
map.put("winter", "12345");
Scanner scanner = new Scanner(System.in);
while(true) {
System.out.println("아이디와 비밀번호를 입력하세요.");
System.out.println("아이디: ");
String id = scanner.nextLine();
System.out.println("비밀번호: ");
String password = scanner.nextLine();
System.out.println();
if(map.containsKey(id)) {
if(map.get(id).equals(password)) {
System.out.println("로그인 되었습니다.");
break;
}
else {
System.out.println("비밀번호가 일치하지 않습니다.");
}
}
else {
System.out.println("입력하신 아이디가 존재하지 않습니다.");
}
}
}
}
아이디와 비밀번호를 입력하세요.
아이디: summer
비밀번호: 123로그인 되었습니다.
컬렉션 프레임워크는 LIFO 자료구조를 제공하는 Stack 클래스와 FIFO 자료구조를 제공하는 Queue 인터페이스를 제공한다.
Stack 클래스는 LIFO 자료구조를 구현한 클래스이다.
| 리턴 타입 | 메소드 | 설명 |
|---|---|---|
| E | push(E item) | 주어진 객체를 스택에 넣는다. |
| E | peek() | 스택의 맨 위 객체를 가져온다. 객체를 스택에서 제거하지 않는다. |
| E | pop() | 스택의 맨 위 객체를 가져온다. 객체를 스택에서 제거한다. |
Stack<E> stack = new Stack<E>(); Stack<E> stack = new Stack<>();
public class Coin {
private int value;
public Coin(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
import java.util.*;
public class Test {
public static void main(String[] args) {
Stack<Coin> coinBox = new Stack<Coin>();
coinBox.push(new Coin(100));
coinBox.push(new Coin(50));
coinBox.push(new Coin(500));
coinBox.push(new Coin(10));
while(!coinBox.isEmpty()) {
Coin coin = coinBox.pop();
System.out.println("꺼내온 동전 : " +coin.getValue() + "원" );
}
}
}
꺼내온 동전 : 10원
꺼내온 동전 : 500원
꺼내온 동전 : 50원
꺼내온 동전 : 100원
Queue 인터페이스는 FIFO 자료구조에서 사용되는 메소드를 정의하고 있다.
| 리턴 타입 | 메소드 | 설명 |
|---|---|---|
| boolean | offer(E e) | 주어진 객체를 넣는다. |
| E | peek() | 객체를 하나 가져온다. 객체를 큐에서 제거하지 않는다. |
| E | poll() | 객체를 하나 가져온다. 객체를 큐에서 제거한다. |
Queue 인터페이스를 구현한 대표적인 클래스는 LinkedList이다. LinkedList는 List 인터페이스를 구현했기 때문에 List 컬렉션이기도하다.
Queue<E> queue = new Queue<E>(); Queue<E> queue = new Queue<>();
public class Message {
public String command;
public String to;
public Message(String command, String to) {
this.command = command;
this.to = to;
}
}
import java.util.LinkedList;
import java.util.Queue;
public class Test {
public static void main(String[] args) {
Queue<Message> messageQueue = new LinkedList<Message>();
messageQueue.offer(new Message("sendMail", "홍길동"));
messageQueue.offer(new Message("seneSMS", "제이크"));
messageQueue.offer(new Message("sendKakaotalk", "미니언"));
while(!messageQueue.isEmpty()) {
Message message = messageQueue.poll();
switch(message.command) {
case "sendMail":
System.out.println(message.to + "님에게 메일을 보냅니다.");
break;
case "seneSMS":
System.out.println(message.to + "님에게 메일을 보냅니다.");
break;
case "sendKakaotalk":
System.out.println(message.to + "님에게 메일을 보냅니다.");
break;
}
}
}
}
홍길동님에게 메일을 보냅니다.
제이크님에게 메일을 보냅니다.
미니언님에게 메일을 보냅니다.