[컬렉션 프레임 워크]
컬렉션이란, 여러 데이터의 집합 혹은 여러 데이터를 그룹으로 묶어놓은 것을 나타낸다.
컬렉션 프레임워크는 이러한 컬렉션을 다루는데 필요한 메서드들을 미리 정의해 놓은 것을 나타낸다.
즉, 특정 자료 구조에 데이터를 추가하고, 삭제하고, 수정하고, 검색하는 동작을 수행하는
편리한 메서드들을 미리 정의해 놓고 이를 제공하는 기능을 #컬렉션 프레임워크 라고 한다.
#컬렉션 프레임워크에서 제공하는 주요 인터페이스로는 #List, #Set, #Map이 있으며,
이 중 #List와 #Set은 유사한 점이 많아 #Collection 이라는 인터페이스로 분류된다.
#Collection 공통 인터페이스 (List와 Set 모두 사용 가능)
기능 | 리턴 타입 | 메소드 | 설명 |
객체 추가 | boolean | add(Object o) / addAll(Collection c) | 주어진 객체 및 컬렉션의 객체들을 컬렉션에 추가 |
객체 검색 | boolean | contatins(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[] | toArray(Object[] a) | 주어진 배열에 컬렉션의 객체를 저장해서 반환 | |
Object | 객체명.toString() | 객체에 저장된 모든 내용을 문자열로 변경 |
A. #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를 정렬 |
[특징]
1. 데이터의 순서가 유지되며, 중복 저장이 가능한 컬렉션을 구현하는데 사용.
2. ArrayList, Vector, Stack, LinkedList 등이 해당한다.
[대표적인 List]
[A] ArrayList
1. 객체를 추가하면 객체가 인덱스로 관리된다.
2. 저장 용량을 초과하면 자동으로 용량이 늘어나게 된다.
3. 데이터가 연속적으로 존재하게 되어 데이터의 순서를 유지한다.
- ArrayList 생성 예시 -
ArrayList<타입> 객체명 = new ArrayList<타입>(초기_저장_용량); // 초기_저장_용량 default = 10
ArrayList<타입 매개변수> 객체명 = new ArrayList<타입 매개변수>(초기 저장 용량);
ArrayList<String> container1 = new ArrayList<String>();
// String 타입의 객체를 저장하는 ArrayList 생성
// 초기 용량이 인자로 전달되지 않으면 기본적으로 10으로 지정됩니다.
ArrayList<String> container2 = new ArrayList<String>(30);
// String 타입의 객체를 저장하는 ArrayList 생성
// 초기 용량을 30으로 지정하였습니다.
이후 ArrayList에 객체를 추가하면 인덱스 0번부터 차례대로 저장되며,
특정 인덱스의 객체를 제거하게 되면, 제거된 인덱스 객체의 바로 뒤 인덱스부터
마지막 인덱스까지 모두 앞으로 1칸씩 당겨져 전체 객체 인덱스 배열에 변화가 생긴다.
즉, 빈번한 객체 삭제와 삽입이 일어나는 곳은 ArrayList보다는 LinkedList를 사용.
- ArrayList 객체 추가, 검색, 삭제 예시 -
public class ArrayListExample {
public static void main(String[] args) {
// ArrayList를 생성하여 list에 할당
ArrayList<String> list = new ArrayList<String>();
// 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);
}
}
[B] LinkedList
저장되는 모든 데이터가 불규칙적으로 존재하며, 서로 연결되어 있다.
LinkedList의 각 요소들은 자신과 연결된 이전 요소 및 다음 요소의 주소값과 데이터로 구성되어 있다.
[특징]
1. 중간에 데이터를 추가하거나 삭제해도 인덱스가 밀리는 현상이 없어 처리가 빠르다.
2. 불연속적으로 존재하며 데이터들이 서로 연결되어 있다.
3. 데이터를 효율적으로 추가, 삭제, 변경의 동작을 하기 위해 사용한다.
[LinkedList에서 데이터 삭제하는 방법]
삭제하고자 하는 요소의 이전 요소가 삭제하고자 하는 요소의 다음 요소를 참조하도록 변경
ex) Orange를 삭제할 경우,
Apple 의 Next가 Banana를 참조하고, Banana의 Prev요소가 Apple을 참조.
[LinkedList에서 데이터 추가하는 방법]
새로운 요소를 추가하고자 하는 위치의 이전 요소와 다음 요소 사이를 연결해준다.
ex) Mango를 Apple과 Orange 사이에 추가할 경우,
Apple의 Next가 Mango를 참조, Mange의 Prev 요소가 Apple을 참조,
Mango의 Next 요소가 Orange를 참조.
즉, 배열처럼 데이터를 이동하기 위해 복사할 필요가 없으므로 처리 속도가 빠르다.
[C] ArrayList 와 LinkedList 비교
[ArrayList 강점]
1. 데이터를 순차적으로 추가하거나 삭제하는 경우 처리가 빠르다.
2. 데이터를 불러올 경우 인덱스로 관리하므로 처리가 빠르다.
[ArrayList 약점]
1. 중간 위치에 데이터를 추가하거나, 삭제할 때 전체 배열이 인덱스만큼 움직이므로 느리다.
[LinkedList 강점]
1. 중간 위치에 데이터를 추가하거나 삭제하는 경우 인덱스로 관리되는게 아니므로 속도가 빠르다.
[LinkedList 약점]
1. 데이터를 불러올 경우, 인덱스가 없어 전체 데이터를 탐색해야 하므로 속도가 느리다.
<결론>
데이터의 잦은 변경이 예상된다 > LinkedList
데이터의 개수가 변하지 않는다 > ArrayList
B. #Set 인터페이스
수학에서 Set은 집합을 의미하며 일반적으로 중복된 값을 허용하지 않는다.
Java Collection에서의 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) | 주어진 객체를 삭제한다. |
[특징]
1. 데이터의 저장 순서가 유지되지 않고, 요소의 중복을 허용하지 않는다.
2. HashSet, TreeSet 등이 대표적인 Set 인터페이스를 구현한다.
[A] HashSet
중복되지 않은 데이터를 순서에 상관없이 저장하는 Set 클래스.
데이터를 입력 시, 내부적으로 기존 저장된 객체 중 같은 hashCode()를 찾아서,
같으면 equals() 메서드를 통해 동일 객체인지 판단 후,
동일 객체면 저장되지 않고 동일 객체가 아닐 때만 데이터가 저장되는 구조입니다.
1. 중복 값 허용 X
2. 저장 순서 유지 X.
3. Null 값 허용 O.
4. 자동 정렬 X.
[예제 코드]
public class Main {
public static void main(String[] args) {
// HashSet 생성
HashSet<String > languages = new HashSet<String>();
// 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());
}
}
}
Set은 인덱스로 객체를 가져오는 get() 메서드가 없고, 전체 객체를 대상으로 한번씩 반복해서 가져오는
반복자 Iterator()를 제공하며 Iterator에서 하나의 객체를 가져올 때는 next() 메서드를 사용한다.
Iterator()에서 하나의 객체를 가져올 때는 next() 메서드를 사용한다.
1. Iterator 객체명 = set객체.iterator();
2. 객체명.next();
[B] TreeSet
정렬 특화되어 있으며, 최대값, 최솟값 반환 메서드와 통계 메서드를 제공.
기본 오름차순 정렬 방식의 #이진탐색트리 형태로 데이터를 저장.
1. 중복 값 허용 X
2. 저장 순서 유지 X.
3. 자동 정렬 O.
#이진 탐색 트리
정렬과 검색에 특화된 자료 구조.
1개의 부모 노드(루트)가 최대 2개의 자식 노드와 연결되는 이진 트리.
부모 노드 기준 #좌측은 부모 노드보다 작은 값이 저장되고, #우측은 큰 값이 저장되는 특징이 있다.
[예제 코드]
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("Lee"));
System.out.println(workers.subSet("Kim", "Park"));
}
}
first() : 가장 큰 값 반환
last() : 가장 작은 값 반환
lower : 제공된 값보다 작은 값들 중 가장 큰 값 반환
higher : 제공된 값보다 큰 값들 중 가장 작은 값 반환
floor : 제공된 값과 같거나 작은 값 중 가장 큰 값 반환
ceiling : 제공된 값보다 크거나 같은 값 중, 가장 작은 값 반환
subSet : 1번째 인자값 범위(포함)부터 2번째 인자값 범위(불포함)까지 반환
C. #Map 인터페이스
Key와 Value 형태로 구성된 Entry 객체를 저장하는 구조.
기능 | 리턴 타입 | 메서드 | 설명 |
객체 추가 | Object | put(Object key, Object value) | 주어진 키로 값을 저장. 해당 키가 새로운 키일 경우,null을 리턴, 같은 키가 있으면 기존의 값을 대체하고, 대체 이전의 값을 리턴 |
객체 검색 | boolean | containsKey(Object key) | 주어진 Key가 있으면 true, 없으면 false 리턴 |
boolean | containsValue(Object value) | 주어진 Value가 있으면 true, 없으면 false 리턴 | |
Set | entrySet() | Key와 Value 쌍으로 구성된 모든 Entry 객체를 Set에 담아서 리턴 | |
Object | get(Object key) | 주어진 key에 해당하는 Value를 리턴 | |
boolean | isEmpty() | 컬렉션이 비어있는지 확인 | |
Set | keySet() | 모든 Key를 Set 객체에 담아서 리턴. | |
int | size() | 저장된 Entry 객체의 총 갯수를 리턴. | |
Collection | values() | 저장된 모든 Values을 Collection에 담아서 리턴 | |
객체 삭제 | void | clear() | 모든 Map.Entry(Key 와 Value) 삭제 |
Object | remove(Object key) | 주어진 Key와 일치하는 Map.Entry를 삭제하고 Value 리턴. |
[특징]
1. Key와 Value의 형태로 데이터를 저장하는 컬렉션.
2. 데이터의 순서가 유지되지 않는다.
3. Key는 식별하기 위해 사용되므로 중복 사용 불가능.
4. Value는 중복 저장 가능.
5. HashMap, HashTable, TreeMap, Properties등의 형태가 있다.
[대표적인 Map 인터페이스]
[A] HashMap
[특징]
1. 많은 양의 데이터를 검색하는데 있어서 뛰어난 성능을 보인다.
2. Key와 Value로 구성된 Entry 객체를 저장한다.
HashMap의 개별 요소인 Entry 객체는 Map 인터페이스의 내부 인터페이스인 Entry 인터페이스를 가진다.
즉, 다음과 같은 Map.Entry 인터페이스의 메서드 또한 사용 가능하다.
리턴 타입 | 메서드 | 설명 |
boolean | equals(Object o) | 동일한 Entry 객체인지 비교 |
Object | getKey() | Entry 객체의 Key 객체를 반환 |
Object | getValue() | Entry 객체의 Value 객체를 반환 |
int | hashCode() | Entry 객체의 해시코드를 반환 |
Object | setValue(Object value) | Entry 객체의 Value 객체를 인자로 전달한 Value 객체로 변환 |
Map은 Key와 Value 쌍으로 저장되는 객체이므로 Iterator()를 호출할 수 없다.
대신 keySet()이나 entrySet()으로 Set 인터페이스 형태로 반환한 뒤,
iterator()를 호출하여 반복자를 통해 순회할 수 있다.
[B] HashTable
HashMap과 내부 구조가 동일하며, 사용 방법 또한 매우 유사하지만 하기와 같이 차이점이 있다.
[C] HashMap 과 HashTable 차이
1. Thread-safe 여부.
#HashMap은 thread-safe X, , #HashTable은 thread-safe O
즉, 멀티 스레드 환경이 아니라면, HashTable은 HashMap보다 성능이 떨어진다는 단점이 있다.
#Thread Safe
멀티 스레드 환경에서 일반적으로 어떤 함수나, 변수, 객체가
여러 스레드로부터 동시에 접근이 이루어져도 프로그램 실행에 문제가 없음을 나타냄.
2. null 값 허용 여부.
#HashMap은 Key에 null 허용 O, #HashTable은 key에 null 허용 X
[정리]
#List
1. (ArrayList의 경우)저장되는 객체를 #인덱스로 관리한다. = (LinkedList는 #인덱스 관리 X)
2. (ArrayList의 경우)List는 데이터의 순서가 유지되며, 중복 저장이 가능한 컬렉션.
2. ArrayList, Vector, Stack, LinkedList 등이 대표적이다.
#Set
1. Set은 데이터의 순서가 유지되지 않으며, 중복 저장이 불가능한 컬렉션.
2. HashSet, TreeSet 등이 대표적이다.
> 객체를 인덱스로 관리하는 #List에 비해 #Set의 경우 데이터 추가, 조회, 삭제의 경우 속도가 느리다.
#Map
1. Map은 데이터를 Key, Value 쌍의 형태로 저장하는 컬렉션.
2. 데이터의 순서가 유지되지 않으며, #Key는 식별자로 사용되므로 중복 허용 X, #Value는 중복 허용 O.
3. HashMap, HashTable 등이 대표적이다.
'백엔드 기술 > Java' 카테고리의 다른 글
컬렉션과 스트림의 차이 (0) | 2023.04.19 |
---|---|
자료 구조에서 Array VS List (0) | 2023.04.18 |
생성자란 ? (0) | 2023.04.17 |
이너 클래스의 장점 (0) | 2023.04.17 |
접근 제어자 (0) | 2023.04.17 |