반응형
☕️ 컬렉션 프레임워크(Collection Framework)란?
컬렉션 프레임워크(collection framework)란 다수의 데이터를 쉽고 효과적으로 처리할 수 있는 표준화된 방법을 제공하는 클래스의 집합을 의미한다. 컬렉션 프레임워크의 주요 인터페이스에는 List, Set, Map이 있으며 각 각의 인터페이스들은 구현체를 가지고 있다.
각 각의 인터페이스의 주요 특징으로는
- List
- 순서가 있는 데이터 집합이다.
- 데이터의 중복을 허용한다. - Set
- 순서가 없는 데이터 집합이다.
- 데이터의 중복을 허용하지 않는다. - Map
- 키와 값의 한 쌍으로 이루어진 데이터 집합으로 순서가 없다.
- 키의 중복은 허용하지 않고, 값의 중복은 허용한다.
☕️ List 인터페이스
List 인터페이스는 저장 순서를 유지해 주고, 데이터의 중복을 허용한다.
List 인터페이스를 구현하는 대표적인 클래스에는 ArraysList, LinkedList, Vector가 있다.
- ArrayList
- 단방향 포인터 구조로 인덱스로 접근하므로 조회할 때 유리하지만, 데이터의 삽입이나 삭제가 발생할 경우 인덱스를 조정하는 추가 작업이 필요하다. - LinkedList
- 순차 접근만 가능하여 조회할 때는 불리하지만, 양방향 포인터 구조로 데이터의 삽입, 삭제에 유리하다. - Vector
- ArrayList와 구현 원리와 기능적인 측면에서 동일하며, 동기화를 지원한다.
주요 메서드 | 설명 |
boolean add(E e) | 주어진 객체를 맨 끝에 추가 |
void add(int index, E element) | 주어진 인덱스에 객체를 추가 |
set(int index, E element) | 주어진 인덱스에 저장된 객체를 주어진 객체로 변경 |
boolean contains(Object o) | 주어진 객체가 있는지에 대한 여부를 검색 |
E get(int index) | 주어진 인덱스에 저장된 객체를 리턴 |
isEmpty() | 컬렉션이 비어있는지 확인 |
int size() | 저장되어 있는 전체 객체 수를 리턴 |
E remove(int index) | 주어진 인덱스에 저장된 객체를 삭제 |
void clear() | 저장되어 있는 객체 모두 비우기 |
boolean remove(Object o) | 주어진 객체 삭제 |
List 예제코드
더보기
import java.util.*;
public class ListEx {
public static void main(String[] args) {
/* ArrayList */
List<String> arrayList = new ArrayList<>();
// 요소 추가
arrayList.add("가");
arrayList.add("나");
arrayList.add("다");
System.out.println(arrayList); // [가, 나, 다]
// 인덱스를 통한 요소 추가
arrayList.add(0, "라");
System.out.println(arrayList); // [라, 가, 나, 다]
// 값 변경
arrayList.set(0, "마");
System.out.println(arrayList); // [마, 가, 나, 다]
// 인덱스를 통한 요소 삭제
arrayList.remove(0);
System.out.println(arrayList); // [가, 나, 다]
// 값을 통한 요소 삭제
arrayList.remove("나");
System.out.println(arrayList); // [가, 다]
// 배열로 값 반환
Object[] obj = arrayList.toArray();
System.out.println(Arrays.toString(obj)); // [가, 다]
// 초기화 전
System.out.println(arrayList); // [가, 다]
System.out.println(arrayList.size()); // 2
System.out.println(arrayList.isEmpty()); // false
// 초기화 후
arrayList.clear();
System.out.println(arrayList); // []
System.out.println(arrayList.size()); // 0
System.out.println(arrayList.isEmpty()); // true
// 인덱스 및 값을 통한 요소 삭제2
List list2 = null;
// 인덱스로 삭제
list2 = new ArrayList<>();
list2.add("1");
list2.add(2);
list2.add(1);
System.out.println(list2); // [1, 2, 1]
list2.remove(1);
System.out.println(list2); // [1, 1] : 1번째 인덱스인 2가 삭제됨.
// 요소로 삭제
list2 = new ArrayList<>();
list2.add("1");
list2.add(2);
list2.add(1);
System.out.println(list2); // [1, 2, 1]
list2.remove(new Integer(1));
System.out.println(list2); // [1, 2] : 숫자 1이 삭제됨.
// A.retainAll(B) : A의 요소 중 B에 존재하는 요소만 남기고 삭제한다.
List<String> listA = null;
List<String> listB = null;
// listA.retainAll(listB);
listA = new ArrayList<>();
listA.add("A"); listA.add("B"); listA.add("C"); listA.add("H");
listB = new ArrayList<>();
listB.add("B"); listB.add("C"); listB.add("D");
System.out.println(listA); // [A, B, C, H]
System.out.println(listB); // [B, C, D]
listA.retainAll(listB);
System.out.println(listA); // [B, C]
System.out.println(listB); // [B, C, D]
// listB.retainAll(listA);
listA = new ArrayList<>();
listA.add("A"); listA.add("B"); listA.add("C"); listA.add("H");
listB = new ArrayList<>();
listB.add("B"); listB.add("C"); listB.add("D");
System.out.println(listA); // [A, B, C, H]
System.out.println(listB); // [B, C, D]
listB.retainAll(listA);
System.out.println(listA); // [A, B, C, H]
System.out.println(listB); // [B, C]
// Arrays.asList()를 사용한 ArrayList 생성
List<String> arraysAsList = new ArrayList(Arrays.asList("A", "B", "C", "D"));
/* Vector - ArrayList와 유사하나 동기화 메서드로 구현되어 있어 멀티 쓰레드에 적합하다. */
List<String> vector = new Vector<>();
/* LinkedList - ArrayList와 유사하나 인덱스로 저장하는게 아닌 앞뒤 객체의 정보를 저장한다. */
List<String> linkedList = new LinkedList<>();
/*
ArrayList와 LinkedList 성능비교
1. ArrayList - 검색 속도 빠름, 추가&삭제 느림(순차적 추가&삭제는 더 빠르다.)
* ArrayList의 요소를 순차적으로 삭제할 경우, 뒤에서부터 순차적으로 삭제한다면 배열의 복사가 발생하지 않는다.
2. LinkedList - 검색 속도 느림, 추가&삭제 빠름
*/
}
}
☕️ Set 인터페이스
Set 인터페이스는 저장 순서를 유지하지 않고, 데이터 중복을 허용하지 않는다.
Set 인터페이스를 구현하는 대표적인 클래스에는 HashSet, TreeSet, LinkedHashSet이 있다.
- HashSet
- 가장 빠르게 접근할 수 있으나 순서를 보장하지 않는다. - TreeSet
- 입력한 순서대로 값을 저장하지 않지만, 기본적으로 오름차순으로 정렬한다. - LinkedHashSet
- 입력한 순서대로 값을 저장한다.
주요 메서드 | 설명 |
boolean add(E e) | 주어진 객체를 저장 후 성공이면 true를 중복 객체면 false를 리턴 |
boolean contains(Object o) | 주어진 객체가 있는지에 대한 여부를 검색 |
Iterator<E> iterator() | 저장된 객체를 한 번씩 가져오는 반복자(Iterator)를 리턴 |
isEmpty() | 컬렉션이 비어있는지 확인 |
int size() | 저장되어 있는 전체 객체 수를 리턴 |
void clear() | 저장되어 있는 객체 모두 비우기 |
boolean remove(Object o) | 주어진 객체 삭제 |
Set 예제코드
더보기
import java.util.*;
public class SetEx {
public static void main(String[] args) {
/* HashSet */
Set<String> hashSet = new HashSet<>();
// 요소 추가
hashSet.add("가");
hashSet.add("나");
hashSet.add("가"); // 중복을 허용하지 않는다.
hashSet.add("다");
System.out.println(hashSet); // [가, 다, 나] : HashSet은 추가한 순서대로 출력되지 않는다.
// 요소 삭제
hashSet.remove("다");
System.out.println(hashSet); // [가, 나] : HashSet은 추가한 순서대로 출력되지 않는다.
// 요소 확인
System.out.println(hashSet.contains("나")); // true
System.out.println(hashSet.contains("다")); // false
// 모든 요소 출력
Iterator<String> it = hashSet.iterator();
while (it.hasNext()) {
System.out.println(it.next()); // 저장되어있던 가, 나를 하나씩 출력
}
/* LinkedHashSet - HashSet과 유사하나 입력 순서가 보장된다. */
Set<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("라");
linkedHashSet.add("다");
linkedHashSet.add("나");
linkedHashSet.add("가");
System.out.println(linkedHashSet); // [라, 다, 나, 가]
/* TreeSet - HashSet과 유사하나 정렬 및 검색 기능이 추가되었다. */
TreeSet<String> treeSet = new TreeSet<>();
treeSet.add("라");
treeSet.add("바");
treeSet.add("나");
treeSet.add("다");
treeSet.add("가");
treeSet.add("마");
System.out.println(treeSet); // [가, 나, 다, 라, 마, 바] : 크기 순으로 출력
System.out.println(treeSet.first()); // 가 : 원소 중 가장 작은 원소값 출력
System.out.println(treeSet.last()); // 바 : 원소 중 가장 큰 원소값 출력
// 데이터 꺼내기
System.out.println(treeSet.pollFirst()); // 가
System.out.println(treeSet.pollFirst()); // 나
System.out.println(treeSet.pollLast()); // 바
System.out.println(treeSet); // [다, 라, 마] : pollFirst() 또는 pollLast()를 통해 데이터를 꺼내면 저장된 원소에서 제거된다.
// 데이터 정렬
NavigableSet<String> navigableSet = treeSet.descendingSet();
System.out.println(navigableSet); // [마, 라, 다]
System.out.println(navigableSet.descendingSet()); // [다, 라, 마]
// 부분 집합 생성
treeSet.add("가");
treeSet.add("나");
treeSet.add("바");
System.out.println(treeSet); // [가, 나, 다, 라, 마, 바]
System.out.println(treeSet.headSet("다")); // [가, 나] : 파라미터 미만 데이터(포함x)
System.out.println(treeSet.headSet("다", true)); // [가, 나, 다] : 파라미터 이하 데이터(포함o)
System.out.println(treeSet.tailSet("다")); // [다, 라, 마, 바] : 파라미터 이상 데이터(포함o)
System.out.println(treeSet.tailSet("다", false)); // [라, 마, 바] : 파리미터 초과 데이터(포함x)
System.out.println(treeSet.subSet("나", "라")); // [나, 다]
System.out.println(treeSet.subSet("나", true, "라", true)); // [나, 다, 라]
// 객체 데이터 예제 - equals()와 hashCode() 오버라이딩
Set<Person> hashSet1 = new HashSet<>();
hashSet1.add(new Person("choi", 20));
hashSet1.add(new Person("kim", 31));
hashSet1.add(new Person("choi", 31));
hashSet1.add(new Person("choi", 20));
hashSet1.add(new Person("kim", 40));
Iterator<Person> iterator1 = hashSet1.iterator();
while (iterator1.hasNext()) {
System.out.println(iterator1.next());
}
/*
choi(20)
kim(31)
choi(31)
kim(40)
*/
// 객체 데이터 예제 - equals()와 hashCode() 오버라이딩 안함
Set<Person2> hashSet2 = new HashSet<>();
hashSet2.add(new Person2("choi", 20));
hashSet2.add(new Person2("kim", 31));
hashSet2.add(new Person2("choi", 31));
hashSet2.add(new Person2("choi", 20));
hashSet2.add(new Person2("kim", 40));
Iterator<Person2> iterator2 = hashSet2.iterator();
while (iterator2.hasNext()) {
System.out.println(iterator2.next());
}
/*
kim(40)
choi(20)
choi(31)
kim(31)
choi(20)
*/
// 집합 (합집합, 교집합, 차집합)
Set<Integer> setA = new HashSet<>();
Set<Integer> setB = new HashSet<>();
// 값 초기화
setA.add(1); setA.add(2); setA.add(3); setA.add(4); setA.add(5);
System.out.println(setA); // [1, 2, 3, 4, 5]
setB.add(4); setB.add(5); setB.add(6); setB.add(7); setB.add(8);
System.out.println(setB); // [4, 5, 6, 7, 8]
// 교집합. 공통된 요소만 남기고 삭제
// setA.retainAll(setB);
// System.out.println(setA); // [4, 5]
// 합집합. 모든 요소를 합함
// setA.addAll(setB);
// System.out.println(setA); // [1, 2, 3, 4, 5, 6, 7, 8]
// 차집합. 공통 요소를 제거
setA.removeAll(setB);
System.out.println(setA); // [1, 2, 3]
}
}
class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + "(" + age + ")";
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Person)) {
return false;
}
Person p = (Person)obj;
return this.name.equals(p.name) && this.age == p.age;
}
}
class Person2 {
String name;
int age;
Person2(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + "(" + age + ")";
}
}
☕️ Map 인터페이스
Map 인터페이스는 키(key)와 값(value)으로 구성된 Entry 객체를 저장하는 구조이다. Map은 해싱(hashing)을 사용하기 때문에 방대한 양의 데이터를 검색하는 데 있어서 뛰어난 성능을 보인다. Map 인터페이스를 구현하는 대표적인 클래스에는 TreeMap, HashMap, Hashtable 등이 있다.
- TreeMap
- 데이터를 이진 검색 트리(binary search tree)의 형태로 저장하므로 데이터의 삽입이나 삭제가 빠르다. - HashMap
- 가장 많이 사용되는 클래스 중 하나이며, 해시 알고리즘(hash algorithm)을 사용하여 검색 속도가 매우 빠르다. - Hashtable
- HashMap과 다르게 동기화를 보장한다.
주요 메서드 | 설명 |
V put(K Key, V value) | 주어진 키와 값을 추가하여 저장되면 값을 리턴 |
boolean containsKey(Object Key) | 주어진 키가 있는지 확인 |
boolean containsValue(Object value) | 주어진 값이 있는지 확인 |
Set<Map.Entry<K,V>> entrySet() | 모든 Map.Entry 객체를 Set에 담아 리턴 |
Set<K> keySet() | 모든 키를 Set객체에 담아서 리턴 |
V get(Object key) | 주어진 키에 있는 값을 리턴 |
boolean isEmpty() | 컬렉션이 비어있는지 확인 |
int size() | 저장되어 있는 전체 객체의 수를 리턴 |
Collection<V> values() | 저장된 모든 값을 Collection에 담아서 리턴 |
void clear() | 저장되어 있는 객체 모두 비우기 |
V remove(Object Key) | 주어진 키와 일치하는 Map.Entry를 삭제하고 값을 리턴 |
Map 예제코드
더보기
import java.util.*;
public class MapEx {
public static void main(String[] args) {
/* HashMap */
Map<Integer, String> hashMap = new HashMap<>();
// 요소 추가
hashMap.put(1, "가");
hashMap.put(2, "나");
hashMap.put(3, "다");
hashMap.put(4, "다"); // value는 중복이 가능하다.
hashMap.put(5, "라");
hashMap.put(5, "마"); // key는 중복이 불가능하여 덮어쓰여진다.
System.out.println(hashMap); // {1=가, 2=나, 3=다, 4=다, 5=마}
// key를 통한 값 변경
hashMap.replace(4, "라");
System.out.println(hashMap); // {1=가, 2=나, 3=다, 4=라, 5=마}
// key, value를 통한 값 변경
hashMap.replace(4, "라", "마");
hashMap.replace(5, "바", "사"); // key는 일치하지만 value가 일치하지 않아 변경되지 않는다.
System.out.println(hashMap); // {1=가, 2=나, 3=다, 4=마, 5=마}
// key 확인
System.out.println(hashMap.containsKey(1)); // true
System.out.println(hashMap.containsKey(6)); // false
// value 확인
System.out.println(hashMap.containsValue("가")); // true
System.out.println(hashMap.containsValue("하")); // false
// key를 통한 값 제거
hashMap.remove(1);
System.out.println(hashMap); // {2=나, 3=다, 4=마, 5=마}
// key, value를 통한 값 제거
hashMap.remove(4, "마");
hashMap.remove(5, "바"); // key는 일치하지만 value가 일치하지 않아 제거되지 않는다.
System.out.println(hashMap); // {2=나, 3=다, 5=마}
// clear
hashMap.clear();
/* Hashtable - HashMap과 유사하나 동기화 메서드로 구현되어 있어 멀티 쓰레드에 적합하다. */
Map<Integer, String> hashtable = new Hashtable<>();
/* LinkedHashMap - HashMap과 유사하나 입력 순서가 보장된다. */
hashMap.put(5, "가");
hashMap.put(2, "나");
hashMap.put(1, "다");
System.out.println(hashMap); // {1=다, 2=나, 5=가}
Map<Integer, String> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put(5, "가");
linkedHashMap.put(2, "나");
linkedHashMap.put(1, "다");
System.out.println(linkedHashMap); // {5=가, 2=나, 1=다}
/* TreeMap - HashMap과 유사하나 정렬 및 검색 기능이 추가되었다. */
TreeMap<Integer, String> treeMap = new TreeMap<>();
treeMap.put(1, "아");
treeMap.put(2, "나");
treeMap.put(3, "자");
treeMap.put(4, "가");
treeMap.put(5, "하");
System.out.println(treeMap); // {1=아, 2=나, 3=자, 4=가, 5=하}
// 데이터 검색
System.out.println(treeMap.firstKey()); // 1 : key중 가장 최솟값
System.out.println(treeMap.lastKey()); // 5 : key중 가장 최댓값
System.out.println(treeMap.firstEntry()); // 1=아 : key중 가장 최솟값인 쌍
System.out.println(treeMap.lastEntry()); // 5=하 : key중 가장 최댓값인 쌍
// 정렬
System.out.println(treeMap.descendingMap()); // key 기준 내림차순으로 정렬된 쌍
System.out.println(treeMap.descendingKeySet()); // key 기준 내림차순으로 정렬된 key값
// 부분 집합 생성
System.out.println(treeMap.headMap(3)); // {1=아, 2=나}
System.out.println(treeMap.headMap(3, true)); // {1=아, 2=나, 3=자}
System.out.println(treeMap.headMap(3, false)); // {1=아, 2=나}
System.out.println(treeMap.tailMap(3)); // {3=자, 4=가, 5=하}
System.out.println(treeMap.tailMap(3, true)); // {3=자, 4=가, 5=하}
System.out.println(treeMap.tailMap(3, false)); // {4=가, 5=하}
/*
선언과 동시에 초기화
- 이중 중괄호 구문을 사용하여 구현할 수 있으나,
익명의 추가 클래스를 생성하기 때문에 메모리 누수 문제가 발생할 수 있다.
*/
Map<String, String> initMap = new HashMap() {
{
put("choi", "10");
put("choi", "31");
put("kim", "20");
put("lee", "40");
}
};
System.out.println(initMap); // {choi=31, lee=40, kim=20}
}
}
References.
1. Inpa - Java Collections Framework 종류
2. TCPschool - 컬렉션 프레임워크의 개념
3. 코딩팩토리 - [Java] 자바 컬렉션 프레임워크(List, Set, Map) 총정리
4. kai6666.log - [Java] 컬렉션 프레임워크 (Collection Framework)
반응형
'Java' 카테고리의 다른 글
[Java] Set과 equals() 및 hashCode()의 관계 (0) | 2023.08.13 |
---|---|
[Java] 람다식(Lambda Expressions) (0) | 2023.08.08 |
[Java] 제네릭(Generic) (0) | 2023.08.05 |
[Java] 오버로딩(Overloading)과 오버라이딩(Overriding) (0) | 2023.08.02 |
[Java] 추상클래스와 인터페이스 비교 (0) | 2023.07.29 |