랩터
[Java] 컬렉션 프레임워크 (Collection Framework) 본문
학습 목표
- 컬렉션 프레임워크의 핵심 인터페이스를 이해하고 사용할 수 있다.
- 주요 인터페이스와 컬렉션 클래스의 핵심 메서드를 사용할 수 있다.
- 필요에 따라 어떤 인터페이스와 컬렉션 클래스를 사용하는 것이 적합한지 결정할 수 있다.
컬렉션 프레임워크
컬렉션이란 여러 데이터의 집합을 의미합니다. 즉, 여러 데이터를 그룹으로 묶어놓은 것을 컬렉션이라고 하며, 이러한 컬렉션을 다루는 데에 있어 편리한 메서드들을 미리 정의해 놓은 것을 컬렉션 프레임워크라고 합니다
컬렉션 프레임워크는 특정 자료 구조에 데이터를 추가하고, 삭제하고, 수정하고, 검색하는 등의 동작을 수행하는 편리한 메서드들을 제공해 줍니다.
List
- List는 데이터의 순서가 유지되며, 중복 저장이 가능한 컬렉션을 구현하는 데에 사용됩니다.
- ArrayList, Vector, Stack, LinkedList 등이 List 인터페이스를 구현합니다.
Set
- Set은 데이터의 순서가 유지되지 않으며, 중복 저장이 불가능한 컬렉션을 구현하는 데에 사용됩니다.
- HashSet, TreeSet 등이 Set 인터페이스를 구현합니다.
Map
- Map은 키(key)와 값(value)의 쌍으로 데이터를 저장하는 컬렉션을 구현하는 데에 사용됩니다.
- 데이터의 순서가 유지되지 않으며, 키는 값을 식별하기 위해 사용되므로 중복 저장이 불가능하지만, 값은 중복 저장이 가능합니다.
- HashMap, HashTable, TreeMap, Properties 등
Collection 인터페이스
Collection 인터페이스에는 아래와 같은 메서드들이 정의되어 있습니다
기능 리턴 타입 메서드 설명
객체 추가 | boolean | add(Object o) /addAll(Collection c) | 주어진 객체 및 컬렉션의 객체들을 컬렉션에 추가합니다. |
객체 검색 | boolean | contains(Object o) / containsAll(Collection c) | 주어진 객체 및 컬렉션이 저장되어 있는지를 리턴합니다. |
Iterator | iterator() | 컬렉션의 iterator를 리턴합니다. | |
boolean | equals(Object o) | 컬렉션이 동일한지 확인합니다. | |
boolean | isEmpty() | 컬렉션이 비어있는지를 확인합니다. | |
int | size() | 저장된 전체 객체 수를 리턴합니다. | |
객체 삭제 | void | clear() | 컬렉션에 저장된 모든 객체를 삭제합니다. |
boolean | remove(Object o) / removeAll(Collection c) | 주어진 객체 및 컬렉션을 삭제하고 성공 여부를 리턴합니다. | |
boolean | retainAll(Collection c) | 주어진 컬렉션을 제외한 모든 객체를 컬렉션에서 삭제하고, 컬렉션에 변화가 있는지를 리턴합니다. | |
객체 변환 | Object[] | toArray() | 컬렉션에 저장된 객체를 객체배열(Object [])로 반환합니다. |
Object[] | toArray(Object[] a) | 주어진 배열에 컬렉션의 객체를 저장해서 반환합니다. |
List
List 인터페이스는 배열과 같이 객체를 일렬로 늘어놓은 구조로 되어 있습니다. 객체를 인덱스로 관리하기 때문에 객체를 저장하면 자동으로 인덱스가 부여되고, 인덱스로 객체를 검색, 추가, 삭제할 수 있는 등의 여러 기능을 제공합니다.
기능 | 리턴 | 타입 | 메서드 설명 |
객체 추가 | void | add(int index, Object element) | 주어진 인덱스에 객체를 추가 |
boolean | addAll(int index, Collection c) | 주어진 인덱스에 컬렉션을 추가 | |
Object | set(int index, Object element) | 주어진 위치에 객체를 저장 | |
객체 검색 | Object | get(int index) | 주어진 인덱스에 저장된 객체를 반환 |
int | indexOf(Object o) / lastIndexOf(Object o) | 순방향 / 역방향으로 탐색하여 주어진 객체의 위치를 반환 | |
ListIterator | listIterator() / listIterator(int index) | List의 객체를 탐색할 수 있는 ListIterator 반환 / 주어진 index부터 탐색할 수 있는 ListIterator 반환 | |
List | subList(int fromIndex, int toIndex) | fromIndex부터 toIndex에 있는 객체를 반환 | |
객체 삭제 | Object | remove(int index) | 주어진 인덱스에 저장된 객체를 삭제하고 삭제된 객체를 반환 |
boolean | remove(Object o) | 주어진 객체를 삭제 | |
객체 정렬 | void | sort(Comparator c) | 주어진 비교자(comparator)로 List를 정렬 |
List인터페이스에서 많이 사용되는 클래스로는 ArrayList, LinkedList가 있습니다.
ArrayList
ArrayList는 List 인터페이스를 구현한 클래스로, 컬렉션 프레임워크에서 가장 많이 사용됩니다.
ArrayList에 객체를 추가하면 객체가 인덱스로 관리된다는 점에서는 배열과 유사합니다. 그러나 배열은 생성될 때 크기가 고정되며, 크기를 변경할 수 없는 반면, ArrayList는 저장 용량을 초과하여 객체들이 추가되면, 자동으로 저장용량이 늘어나게 됩니다. 또한, 리스트 계열 자료구조의 특성을 이어받아 데이터가 연속적으로 존재합니다. 즉, 데이터의 순서를 유지합니다.
package Collection_FrameWork;
import java.util.ArrayList;
public class ArrayListExample {
public static void main(String[] args) {
//ArrayList를 생성하여 list에 할당
ArrayList<String> list = new ArrayList<>();
//String 타입의 데이터를 ArrayList에 추가
list.add("Java");
list.add("egg");
list.add("tree");
//저장된 총 객체 수 얻기
int size = list.size();
//0번 인덱스의 객체 얻기
String skill = list.get(0);
//저장된 총 객체 수 만큼 조회
for (int i=0; i<list.size(); i++){
String str = list.get(i);
System.out.println(i + ":" + str);
}
//for -each문으로 순회
for (String str : list) {
System.out.println(str);
}
//0번 인덱스 객체 삭제
list.remove(0); // Java삭제
}
}
빈번한 객체 삭제와 삽입이 일어나는 곳에서는 ArrayList보다는 이후에 배우게 되는 LinkedList를 사용하는 것이 좋습니다.
LinkedList
LinkedList 컬렉션은 데이터를 효율적으로 추가, 삭제, 변경하기 위해 사용합니다.
LinkedList에서 데이터를 삭제하려면, 삭제하고자 하는 요소의 이전 요소가 삭제하고자 하는 요소의 다음 요소를 참조하도록 변경하면 됩니다. 링크를 끊어주는 방식이라고 생각하면 됩니다. 배열처럼 데이터를 이동하기 위해 복사할 필요가 없기 때문에 처리 속도가 훨씬 빠릅니다.
ArrayList와 LinkedList 차이
ArrayList는 다음과 같은 상황에 강점을 지닙니다.
- 데이터를 순차적으로 추가하거나 삭제하는 경우
- 순차적으로 추가한다는 것은 0번 인덱스에서부터 데이터를 추가하는 것을 의미합니다.
- 순차적으로 삭제한다는 것은 마지막 인덱스에서부터 데이터를 삭제하는 것을 의미합니다.
- 데이터를 불러오는 경우
- 인덱스를 통해 바로 데이터에 접근할 수 있으므로 검색이 빠릅니다.
다음과 같은 상황에서 ArrayList는 효율적이지 못합니다.
- 중간에 데이터를 추가하거나, 중간에 위치하는 데이터를 삭제하는 경우
- 추가 또는 삭제 시, 해당 데이터의 뒤에 위치한 값들을 뒤로 밀어주거나 앞으로 당겨주어야 합니다.
LinkedList가 강점을 가지는 상황은 다음과 같습니다.
- 중간에 위치하는 데이터를 추가하거나 삭제하는 경우
- 데이터를 중간에 추가하는 경우, Prev와 Next의 주소값만 변경하면 되므로, 다른 요소들을 이동시킬 필요가 없습니다.
데이터의 잦은 변경이 예상된다면 LinkedList를, 데이터의 개수가 변하지 않는다면 ArrayList를 사용하는 것이 좋습니다.
Iterator
Iterator는 직역하면 반복자라는 의미가 있으며, 컬렉션에 저장된 요소들을 순차적으로 읽어오는 역할을 합니다.
Collection 인터페이스에 정의된 iterator()를 호출하면, Iterator 타입의 인스턴스가 반환됩니다.
메서드 설명
hasNext() | 읽어올 객체가 남아 있으면 true를 리턴하고, 없으면 false를 리턴합니다. |
next() | 컬렉션에서 하나의 객체를 읽어옵니다. 이때, next()를 호출하기 전에 hasNext()를 통해 읽어올 다음 요소가 있는지 먼저 확인해야 합니다. |
remove() | next()를 통해 읽어온 객체를 삭제합니다. next()를 호출한 다음에 remove()를 호출해야 합니다. |
ArrayList<String> list = ...;
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()) { // 읽어올 다음 객체가 있다면
String str = iterator.next(); // next()를 통해 다음 객체를 읽어옵니다.
...
}
next() 메서드로 가져온 객체를 컬렉션에서 제거하고 싶다면 remove() 메서드를 호출하면 됩니다. next() 메서드는 컬렉션의 객체를 그저 읽어오는 메서드로, 실제 컬렉션에서 객체를 빼내는 것은 아닙니다. 하지만, remove() 메서드는 컬렉션에서 실제로 객체를 삭제합니다.
ArrayList<String> list = ...;
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){ // 다음 객체가 있다면
String str = iterator.next(); // 객체를 읽어오고,
if(str.equals("str과 같은 단어")){ // 조건에 부합한다면
iterator.remove(); // 해당 객체를 컬렉션에서 제거합니다.
}
}
Set
Set은 요소의 중복을 허용하지 않고, 저장 순서를 유지하지 않는 컬렉션입니다. 대표적인 Set을 구현한 클래스에는 HashSet, TreeSet이 있습니다. Set인터페이스에 정의된 메서드들은 다음과 같습니다.
기능 리턴 타입 메서드 설명
객체 추가 | boolean | add(Object o) | 주어진 객체를 추가하고, 성공하면 true를, 중복 객체면 false를 반환합니다. |
객체 검색 | boolean | contains(Object o) | 주어진 객체가 Set에 존재하는지 확인합니다. |
boolean | isEmpty() | Set이 비어있는지 확인합니다. | |
Iterator | Iterator() | 저장된 객체를 하나씩 읽어오는 반복자를 리턴합니다. | |
int | size() | 저장된 전체 객체의 수를 리턴합니다. | |
객체 삭제 | void | clear() | Set에 저장된 모든 객체를 삭제합니다. |
boolean | remove(Object o) | 주어진 객체를 삭제합니다. |
HashSet
HashSet은 Set 인터페이스를 구현한 가장 대표적인 컬렉션 클래스입니다. 따라서, Set 인터페이스의 특성을 그대로 물려받으므로 중복된 값을 허용하지 않으며, 저장 순서를 유지하지 않습니다.
package polymorphism_example.Collection;
import java.util.HashSet;
import java.util.Iterator;
public class Main {
public static void main(String[] args) {
//HashSet 생성
HashSet<String> languages = new HashSet<>();
//HashSet 객체 추가
languages.add("Java");
languages.add("Python");
languages.add("Javascript");
languages.add("C++");
languages.add("Kotlin");
languages.add("Ruby");
languages.add("Java"); // 중복
//반복자 생성하여 it에 할당
Iterator it = languages.iterator();
//반복자를 통해 HashSet을 순회하며 각 요소들을 출력
while (it.hasNext()){
System.out.println(it.next());
}
}
}
//출력값
Java
C++
Javascript
Ruby
Python
Kotlin
TreeSet
TreeSet은 이진 탐색 트리 형태로 데이터를 저장합니다. 데이터의 중복 저장을 허용하지 않고 저장 순서를 유지하지 않는 Set 인터페이스의 특징은 그대로 유지됩니다. 이진 탐색 트리(Binary Search Tree)란 하나의 부모 노드가 최대 두 개의 자식 노드와 연결되는 이진트리(Binary Tree)의 일종으로, 정렬과 검색에 특화된 자료 구조입니다. 모든 왼쪽 자식의 값이 루트나 부모보다 작고, 모든 오른쪽 자식의 값이 루트나 부모보다 큰 값을 가지는 특징이 있습니다.
package polymorphism_example.Collection;
import java.util.TreeSet;
public class TreeSetExample {
public static void main(String[] args) {
//TreeSet 생성
TreeSet<String> workers = new TreeSet<>();
//TreeSet에 요소 추가
workers.add("Lee Java");
workers.add("Park Hacker");
workers.add("Kim Coding");
System.out.println(workers); // 전체출력
System.out.println(workers.first());// 최소값 출력
System.out.println(workers.last());// 최대값 출력
System.out.println(workers.higher("Kim Coding")); // 제공된 값보다 큰 값중 가장 작은 값
System.out.println(workers.subSet("Kim", "Park")); // subSet(E fromElement, E toElement): fromElement (포함)부터 toElement (제외)까지의 요소들을 반환
}
}
자동으로 사전 편찬 순에 따라 오름차순으로 정렬된 것을 확인할 수 있습니다. 이는 TreeSet의 기본 정렬 방식이 오름차순이기 때문입니다.
Map
Map 인터페이스는 키(key)와 값(value)으로 구성된 객체를 저장하는 구조로 되어 있습니다. 여기서 이 객체를 Entry 객체라고 하는데, 이 Entry 객체는 키와 값을 각각 Key 객체와 Value 객체로 저장합니다.
Map을 사용할 때 중요한 사실은 키는 중복으로 저장될 수 없지만, 값은 중복 저장이 가능하다는 것입니다. 이는 키의 역할이 값을 식별하는 것이기 때문입니다.
만약 기존에 저장된 키와 같은 키로 값을 저장하면, 기존의 값이 새로운 값으로 대치됩니다.
Map 인터페이스를 구현한 클래스에서 공통으로 사용할 수 있는 메서드입니다.
기능 리턴 타입 메서드 설명
객체 추가 | Object | put(Object key, Object value) | 주어진 키로 값을 저장합니다. 해당 키가 새로운 키일 경우 null을 리턴하지만, 같은 키가 있으면 기존의 값을 대체하고 대체되기 이전의 값을 리턴합니다. |
객체 검색 | boolean | containsKey(Object key) | 주어진 키가 있으면 true, 없으면 false를 리턴합니다. |
boolean | containsValue(Object value) | 주어진 값이 있으면 true, 없으면 false를 리턴합니다. | |
Set | entrySet() | 키와 값의 쌍으로 구성된 모든 Map.Entry 객체를 Set에 담아서 리턴합니다. | |
Object | get(Object key) | 주어진 키에 해당하는 값을 리턴합니다. | |
boolean | isEmpty() | 컬렉션이 비어 있는지 확인합니다. | |
Set | keySet() | 모든 키를 Set 객체에 담아서 리턴합니다. | |
int | size() | 저장된 Entry 객체의 총 갯수를 리턴합니다. | |
Collection | values() | 저장된 모든 값을 Collection에 담아서 리턴합니다. | |
객체 삭제 | void | clear() | 모든 Map.Entry(키와 값)을 삭제합니다. |
Object | remove(Object key) | 주어진 키와 일치하는 Map.Entry를 삭제하고 값을 리턴합니다. |
HashMap
HashMap은 Map 인터페이스를 구현한 대표적인 클래스입니다. HashMap은 아래 그림과 같이 키와 값으로 구성된 객체를 저장하는데, 이 객체를 Entry 객체라고 합니다.
HashMap은 해시 함수를 통해 '키'와 '값'이 저장되는 위치를 결정하므로, 사용자는 그 위치를 알 수 없고, 삽입되는 순서와 위치 또한 관계가 없습니다.
이렇게, HashMap은 이름 그대로 해싱(Hashing)을 사용하기 때문에 많은 양의 데이터를 검색하는데 있어서 뛰어난 성능을 보입니다.
package Collection_FrameWork;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class HashMapExample {
public static void main(String[] args) {
//HashMap 생성
HashMap<String, Integer> map = new HashMap<>();
//Entry 객체 저장
map.put("피카츄", 85);
map.put("꼬부기", 95);
map.put("야도란", 75);
map.put("파이리", 65);
map.put("피존투", 15);
//저장된 총 Entry 수 얻기
System.out.println("총 entry 수: " + map.size());
//객체 찾기
System.out.println("파이리 : " + map.get("파이리"));
//key를 요소로 가지는 Set을 생성 -> 아래에서 순회하기 위해 필요합니다.
Set<String> keySet = map.keySet();
//keySet을 순회하면서 value를 읽어옵니다.
Iterator<String> keyIterator = keySet.iterator();
while (keyIterator.hasNext()){
String key = keyIterator.next();
Integer value = map.get(key);
System.out.println(key + " : " + value);
}
//객체 삭제
map.remove("피존투");
System.out.println("총 entry 수: " + map.size());
//Entry 객체를 요소로 가지는 Set을 생성 -> 아래에서 순회하기 위해 필요합니다.
Set<Map.Entry<String,Integer>> entrySet = map.entrySet();
//entrySet을 순회하면서 value를 읽어옵니다.
Iterator<Map.Entry<String, Integer>> entryIterator = entrySet.iterator();
while (entryIterator.hasNext()){
Map.Entry<String, Integer> entry = entryIterator.next();
String key = entry.getKey(); //Map.Entry 인터페이스의 메서드
Integer value = entry.getValue(); // Map.Entry 인터페이스의 메서드
System.out.println(key + " : " + value);
}
//객체 전체 삭제
map.clear();
}
}
컬렉션 클래스 정리
'공부 > JAVA' 카테고리의 다른 글
[Java] 스트림 - 핵심 개념과 특징 (0) | 2024.07.08 |
---|---|
[Java] 람다 (1) | 2024.07.05 |
[Java] 예외 처리 (0) | 2024.07.03 |
[Java] 제네릭 (0) | 2024.07.03 |
[Java] 열거형 (0) | 2024.07.03 |