15.4 Map 컬렉션
키(key)와 값(value)으로 구성된 엔트리(Entry) 객체를 저장.
키는 중복 저장 X, 값은 중복 저장 O
기존의 저장된 키와 동일한 키로 값을 저장하면 기존 값은 없어지고 새로운 값으로 대치됨.
기능 | 메소드 | 설명 |
객체 추가 | V put(K key, V value) | 주어진 키와 값을 추가, 저장 되면 값을 리턴 |
객체 검색 | boolean containsKey(Object key) | 주어진 키가 있는지 확인 |
boolean containsValue(Object value) | 주어진 값이 있는지 확인 | |
Set<Map.Entry<K, V>> entrySet() | 키와 값의 쌍으로 구성된 모든 Map.Entry 객체를 Set에 담아 리턴 | |
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
key로 사용할 객체를 hashCode() 메소드 리턴값이 같고 equals() 메소드가 true를 리턴할 경우, 동일 키로 보고 중복 저장을 허용하지 않음.
// HashMap 컬렉션 생성 방법
Map<K, V> map = new HashMap<K, V>();
Map<K, V> map = new HashMap<>(); // 앞의 파라미터 타입과 동일할 경우 생략 가능
- K는 Key타입, V는 Value타입
사용 예시)
import lombok.*;
@AllArgsConstructor
@Getter
@Setter
@EqualsAndHashCode // 롬복은 클래스의 필드 값이 서로 같으면 같은 HashCode와 equals 값을 리턴
public class Member { // Member 클래스
public String name;
public int age;
}
import java.util.*;
import java.util.Map.Entry;
public class HashMapExample {
public static void main(String[] args) {
// 컬렉션 생성
Map<String, Integer> map = new HashMap<>();
// 엔트리 저장
map.put("홍길동", 85); // 85는 Integer. int로 자동 언박싱이 일어남
map.put("감자바", 95);
map.put("홍자바", 74);
map.put("추자바", 82);
map.put(new String("홍길동"), 90); // 키가 같기 때문에 제일 마지막에 저장한 값 저장
// String은 hashCode()를 문자열에 의해 재정의되어있기 때문에 키값이 동등하게 나옴
// 저장된 엔트리 수
System.out.println("총 Entry 수: " + map.size()); // 4
System.out.println();
// key로 값을 얻기
//System.out.println(map.get("홍길동"));
String key = "홍길동";
int value = map.get(key); // Integer타입이 int로 자동 언박싱이 일어남
System.out.println(key + ":" + value);
System.out.println();
// 엔트리를 하나씩 가져와 키와 값을 출력
for(Entry<String, Integer> entry : map.entrySet()) {
String k = entry.getKey();
int v = entry.getValue();
System.out.println(k + ":" + v);
}
System.out.println();
for(String k : map.keySet()) {
int v = map.get(k);
System.out.println(k + ":" + v);
}
System.out.println();
// 향상된 for문은 '삭제'작업시 문제가 될 수 있다.
/*for(Entry<String, Integer> entry : map.entrySet()) {
String k = entry.getKey();
int v = entry.getValue();
System.out.println(k + ":" + v);
map.remove(k);
}
System.out.println();*/
// Iterator로 사용하기
// 키를 반복하기 위해 반복자를 얻는 것
Iterator<Entry<String, Integer>> iterator = map.entrySet().iterator();
while(iterator.hasNext()) {
Entry<String, Integer> entry = iterator.next();
String k = entry.getKey();
int v = entry.getValue();
if(k.equals("홍길동")) {
iterator.remove();
} else {
System.out.println(k + ":" + v);
}
}
}
}
● Hashtable
HashMap과 동일한 내부 구조를 가짐.
차이점은 동기화된(synchronized) 메소드로 구성되어 멀티 스레드가 동시에 Hashtable의 메소드들을 실행할 수 없다는 것.
Map<K, V> map = new Hashtable<K, V>();
Map<K, V> map = new Hashtable<>();
→ 앞서 배운 Vector부분과 비슷한 예시
● Properties
Properties는 Hashtable의 자식 클래스.
때문에 Hashtable의 특징을 그대로 가지고 있음.
→ 키와 값을 String 타입으로 제한한 컬렉션이다.
주로 확장자가 .properties인 프로퍼티 파일을 읽을 때 사용한다.
프로퍼티 파일은 키와 값이 '=' 기호로 연결되어 있는 텍스트 파일
일반 텍스트 파일과는 다르게 ISO 8859-1문자셋으로 저장, 한글일 경우 \u+유니코드로 표현되어 저장.
// database.properties 파일 텍스트 내용
driver=oracle.jdbc.OracleDriver
username=\uD64D\uAE38\uB3D9
password=12345
// 프로퍼티 사용 예제
import java.io.IOException;
import java.util.Properties;
public class PropertiesExample {
public static void main(String[] args) {
// Properties 컬렉션 생성
Properties prop = new Properties();
// 이 파일과 동일한 ClassPath에 있는 database.properties 파일 로드
try {
prop.load(PropertiesExample.class.getResourceAsStream("database.properties"));
} catch (IOException e) {
}
String userName = prop.getProperty("username");
String password = prop.getProperty("password");
System.out.println(userName + ":" + password);
}
}
출력 → 홍길동:123245
15.5 검색 기능을 강화시킨 컬렉션
컬렉션 프레임워크는 검색 기능을 강화시킨 TreeSet과 TreeMap을 제공함.
TreeSet → Set 컬렉션
TreeMap → Map 컬렉션
● TreeSet
이진 트리(binary tree)를 기반으로 한 Set 컬렉션.
여러 개의 노드(node)가 트리 형태로 연결된 구조.
루트 노드(root node)라고 불리는 하나의 노드에서 시작하여 각 노드에 최대 2개의 노드를 연결할 수 있는 구조를 가지고 있다.
부모 노드보다 작은 값을 가지는 노드는 왼쪽 자식으로, 큰 값을 가지는 노드는 오른쪽 자식으로 배치하여 데이터의 추가나 삭제 시 트리가 한쪽으로 치우쳐지지 않도록 균형을 맞춘다.
TreeSet<E> treeSet = new TreeSet<E>();
TreeSet<E> treeSet = new TreeSet<>();
★ 여기서 E는 비교 가능한(comparable) 객체만 올 수 있다!
Set 타입 변수에 대입해도 되지만 TreeSet 타입으로 대입한 이유는?
→ 검색 관련 메소드가 TreeSet에만 정의되어 있기 때문.
리턴 타입 | 메소드 | 설명 |
E | first() | 제일 낮은 객체를 리턴 |
E | last() | 제일 높은 객체를 리턴 |
E | lower(E e) | 주어진 객체보다 바로 아래 객체 리턴 |
E | higher(E e) | 주어진 객체보다 바로 위 객체 리턴 |
E | floor(E e) | 주어진 객체와 동등한 객체가 있으면 리턴, 만약 없다면 주어진 객체의 바로 아래의 객체를 리턴. |
E | ceiling(E e) | 주어진 객체와 동등한 객체가 있으면 리턴, 만약 없다면 주어진 객체의 바로 위의 객체를 리턴. |
E | pollFirst() | 제일 낮은 객체를 꺼내오고 컬렉션에서 제거함 |
E | pollLast() | 제일 높은 객체를 꺼내오고 컬렉션에서 제거함 |
Iterator<E> | descendingIterator() | 내림차순으로 정렬된 Iterator 리턴 |
NavigableSet<E> | descendingSet() | 내림차순으로 정렬된 NavigableSet 리턴 |
NavigableSet<E> | headSet( E toElement, boolean inclusive ) |
주어진 객체보다 낮은 객체들을 NavigableSet으로 리턴, 주어진 객체 포함 여부는 두 번째 매개값에 따라 달라짐 |
NavigableSet<E> | tailSet( E fromElement, boolean inclusive ) |
주어진 객체보다 높은 객체들을 NavigableSet으로 리턴, 주어진 객체 포함 여부는 두 번째 매개값에 따라 달라짐 |
NavigableSet<E> | subSet( E fromElement, boolean tolnclusive ) |
시작과 끝으로 주어진 객체 사이의 객체들을 NavigableSet으로 리턴. 시작과 끝 객체의 포함 여부는 두 번째, 네 번째 매개값에 따라 달라짐. |
예시)
import java.util.TreeSet;
public class TreeSetExample {
public static void main(String[] args) {
// 컬렉션 생성
TreeSet<Integer> scores = new TreeSet<>();
// 객체 저장
scores.add(87);
scores.add(56);
scores.add(86);
scores.add(23);
scores.add(66);
scores.add(81);
scores.add(96);
// 하나씩 객체를 오름차순으로 가져오기
for(int score : scores) {
System.out.print(score + " ");
}
System.out.println();
// TreeSet이 가지고 있는 메소드
System.out.println("가장 낮은 점수: " + scores.first());
System.out.println("가장 낮은 점수: " + scores.last());
System.out.println("90점 바로 위의 점수: " + scores.lower(86));
System.out.println("90점 바로 아래 점수: " + scores.higher(86));
System.out.println("90이거나 바로 아래 점수: " + scores.floor(86));
System.out.println("90이거나 바로 아래 점수: " + scores.ceiling(86));
System.out.println();
// 하나씩 객체를 오름차순으로 가져오기
for(int score : scores) {
System.out.print(score + " ");
}
System.out.println();
// 하나씩 객체를 내림차순으로 가져오기
for(int score : scores.descendingSet()) {
System.out.print(score + " ");
}
System.out.println();
// 범위 검색(80 <= )
for(int score : scores.tailSet(80, true)) {
System.out.print(score + " ");
}
System.out.println();
// 범위 검색(80 <= score < 90)
for(int score : scores.subSet(80, true, 90, false)) {
System.out.print(score + " ");
}
}
}
● TreeMap
이진 트리를 기반으로 한 Map 컬렉션.
TreeSet과의 차이점 → 키와 값이 저장된 Entry를 저장한다.
키를 기준으로 자동 정렬된다.
부모 키 값과 비교해서 낮은 것은 왼쪽, 높은 것은 오른쪽 자식 노드에 Entry 객체를 저장.
TreeMap<K, V> treeMap = new TreeMap<K, V>();
TreeMap<K, V> treeMap = new TreeMap<>();
★ 여기서 K는 비교 가능한(comparable) 객체만 올 수 있다!
Map 타입 변수에 대입해도 되지만 TreeMap 타입으로 대입한 이유는 검색 관련 메소드가 TreeMap에만 정의되어 있기 때문이다.
리턴 타입 | 메소드 | 설명 |
Map.Entry<K,V> | firstEntry() | 제일 낮은 Map.Entry를 리턴 |
Map.Entry<K,V> | lastEntry() | 제일 높은 Map.Entry를 리턴 |
Map.Entry<K,V> | lowerEntry(K key) | 주어진 키보다 바로 아래 Map.Entry를 리턴 |
Map.Entry<K,V> | higherEntry(K key) | 주어진 키보다 바로 위 Map.Entry를 리턴 |
Map.Entry<K,V> | floorEntry(K key) | 주어진 키와 동등한 키가 있으면 해당 Map.Entry를 리턴, 없으면 바로 아래의 Map.Entry를 리턴. |
Map.Entry<K,V> | ceilingEntry(K key) | 주어진 키와 동등한 키가 있으면 해당 Map.Entry를 리턴, 없으면 바로 위의 Map.Entry를 리턴. |
Map.Entry<K,V> | pollFirstEntry() | 제일 낮은 Map.Entry를 꺼내오고 컬렉션에서 제거 |
Map.Entry<K,V> | pollLastEntry() | 제일 높은 Map.Entry를 꺼내오고 컬렉션에서 제거 |
NavigableSet<K> | desendingKeySet() | 내림차순으로 정렬된 키의 NavigableMap을 리턴. |
NavigableMap<K,V> | descendingMap() | 내림차순으로 정렬된 Map.Entry의 NavigableMap을 리턴. |
NavigableMap<K,V> | headMap( K toKey, boolean inclusive ) |
주어진 키보다 낮은 Map.Entry들을 NavigableMap으로 리턴. 주어진 객체 포함 여부는 두 번째 매개값에 따라 달라짐. |
NavigableMap<K,V> | headMap( K fromKey, boolean inclusive ) |
주어진 키보다 높은 Map.Entry들을 NavigableMap으로 리턴. 주어진 객체 포함 여부는 두 번째 매개값에 따라 달라짐. |
NavigableMap<K,V> | subMap( K fromKey, boolean fromInclusive, K toKey, boolean toInclusive ) |
시작과 끝으로 주어진 키 사이의 Map.Entry들을 NavigableMap 컬렉션으로 반환. 시작과 끝 키의 Map.Entry 포함 여부는 두 번째, 네 번째 매개값에 따라 달라짐. |
예시)
import java.util.*;
import java.util.Map.Entry;
public class TreeMapExample {
public static void main(String[] args) {
// TreeMap 컬렉션 생성
TreeMap<String, Integer> treeMap = new TreeMap<>();
treeMap.put("apple", 10);
treeMap.put("forever", 60);
treeMap.put("description", 40);
treeMap.put("ever", 50);
treeMap.put("zoo", 80);
treeMap.put("base", 20);
treeMap.put("guess", 70);
treeMap.put("monkey", 60);
// 정렬된 엔트리를 하나씩 가져오기
Set<Entry<String, Integer>> entrySet = treeMap.entrySet();
for(Entry<String, Integer> entry : entrySet) {
System.out.println(entry.getKey() + " - " + entry.getValue());
}
System.out.println();
// 특정 키에 대한 값 가져오기
Entry<String, Integer> entry = null;
entry = treeMap.firstEntry();
System.out.println("제일 앞 단어: " + entry.getKey() + " - " + entry.getValue());
entry = treeMap.lastEntry();
System.out.println("제일 뒤 단어: " + entry.getKey() + " - " + entry.getValue());
entry = treeMap.lowerEntry("z"); // 생각해보기
System.out.println("ever 앞 단어: " + entry.getKey() + " - " + entry.getValue());
System.out.println();
// 내림차순으로 정렬
NavigableMap<String, Integer> descendingMap = treeMap.descendingMap();
Set<Entry<String, Integer>> desendingEntrySet = descendingMap.entrySet();
for(Entry<String, Integer> e : desendingEntrySet) {
System.out.println(e.getKey() + " - " + e.getValue());
}
System.out.println();
// 범위 검색
System.out.println("[c~h 사이의 단어 검색]");
NavigableMap<String, Integer> rangeMap = treeMap.subMap("c", true, "h", false);
for(Entry<String, Integer> e : rangeMap.entrySet()) {
System.out.println(e.getKey() + " - " + e.getValue());
}
}
}
● Comparable과 Comparator
TreeSet에 저장되는 객체, TreeMap에 저장되는 키 객체는 저장과 동시에 오름차순으로 정렬되는데
객체가 Comparable(비교가능한) 인터페이스를 구현하고 있어야 가능.
(Integer, Double, String 타입 등은 모두 Comparable을 구현하고 있음)
Comparable 인터페이스의 compareTo() 메소드가 정의되어 있다.
사용자 정의 객체를 저장할 때 반드시 이 메소드를 재정의 해야한다.
리턴 타입 | 메소드 | 설명 |
int | compareTo(T o) | 주어진 객체와 같으면 0 리턴, 주어진 객체보다 적으면 음수 리턴, 주어진 객체보다 크면 양수 리턴. |
예시) 사용자 정의 객체
public class Person implements Comparable<Person> {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Person o) {
int result = name.compareTo(o.name);
// return -result; // 역순으로 출력 활용 가능
return result;
}
}
※ 비교 기능이 없는 Comparable 비구현 객체일 경우, 비교자(Comparator)를 제공해야 함.
리턴 타입 | 메소드 | 설명 |
int | compare(T o1, T o2) | o1과 o2가 동등하면 0 리턴 o1이 o2보다 앞에 오게 하려면 음수 리턴 o1이 o2보다 뒤에 오게 하려면 양수 리턴 |
예시)
import lombok.AllArgsConstructor;
import lombok.Data;
// 롬복 사용
@Data
@AllArgsConstructor
public class Fruit {
public String name;
public int price;
}
// 비교자 클래스
import java.util.Comparator;
public class FruitComparator implements Comparator<Fruit> {
@Override
public int compare(Fruit o1, Fruit o2) {
if(o1.price > o2.price) return +1; // price로 비교
else if(o1.price == o2.price) return 0;
else return -1;
}
}
// 사용
import java.util.TreeSet;
public class ComparatorExample {
public static void main(String[] args) {
// 비교자 객체를 생성하여 선언
TreeSet<Fruit> treeSet = new TreeSet<>(new FruitComparator());
treeSet.add(new Fruit("포도", 3000));
treeSet.add(new Fruit("수박", 10000));
treeSet.add(new Fruit("딸기", 6000));
treeSet.add(new Fruit("바나나", 5000));
// 비교가능한 객체가 되어 값은 반환
for(Fruit t : treeSet) {
System.out.println(t);
}
}
}
15.6 LIFO와 FIFO 컬렉션
▶ LIFO(Last In First Out) : 후입선출. 나중에 넣은 객체가 먼저 빠져나간다.
▶ FIFO(First In First Out) : 선입선출. 먼저 넣은 객체가 먼저 빠져나가는 구조.
● Stack
LIFO 자료구조를 구현한 클래스.
Stack<E> stack = new Stack<E>();
Stack<E> stack = new Stack<>();
리턴 타입 | 메소드 | 설명 |
E | push(E item) | 주어진 객체를 스택에 넣음 |
E | pop() | 스택의 맨 위 객체를 빼냄 |
→ 동전 넣고 빼기의 예시를 보며 이해
● Queue
FIFO 자료구조에서 사용되는 메소드를 정의하고 있는 인터페이스.
Queue 인터페이스를 구현한 대표적 클래스는 LinkedList이다.
따라서
Queue<E> queue = new LinkedList<E>();
Queue<E> queue = new LinkedList<>();
리턴 타입 | 메소드 | 설명 |
boolean | offer(E e) | 주어진 객체를 큐에 넣음 |
E | poll() | 큐에서 객체를 빼냄 |
→ 메시지 예시
15.7 동기화된 컬렉션
컬렉션 프레임워크의 대부분 클래스들은 싱글 스레드 환경에서 사용할 수 있도록 설계.
ArrayLiest, HashSet, HashMap은 멀티 스레드 환경에서 안전하지 않다.
이렇게 비동기화 된 메소드를 동기화된 메소드로 래핑하는 메소드를 제공한다.(synchronizedList 등등)
→ 대략적으로 이해하고 넘어가자.
15.8 수정할 수 없는 컬렉션
컬렉션 생성 시 저장된 요소를 변경하고 싶지 않을 때 유용.
→ 쓸일이 많지 않다. 따라서 대략적으로 이해하고 넘어가자.
'JAVA' 카테고리의 다른 글
19일차 2024 - 3 - 22 (1) | 2024.03.24 |
---|---|
18일차 2024 - 3 - 21 (0) | 2024.03.21 |
16일차 2024 - 3 - 19 (0) | 2024.03.19 |
5일차 2024 - 3 - 4 (0) | 2024.03.18 |
4일차 2024 - 2 - 29 (0) | 2024.03.18 |