- Map : Key, Value 형태로 데이터를 저장하는 자료구조
- HashMap : Map 인터페이스를 구현한 Map 컬렉션 (순서보장X)
- LinkedHashMap : Map 인터페이스를 구현한 Map 컬렉션 (순서보장O)
Map 선언
Map<String, Object> map = new HashMap<String, Object>();
Map value의 자료형이 Object이기 때문에 모든 Object가 다 들어갈 수 있다.
Map<> map = new HashMap<>() 를 사용하는 이유 :
HashMap을 선언 시, Map<String, Object> map = new HashMap<String, Object>(); 과 같은 형식으로 선언한 것을 쉽게 찾아볼 수 있다.
1. 인터페이스니까
▶ 포폴 개발을 진행할 때, List list = new List();와 같이 선언할 수 없었다.
그 이유는 List는 Interface라서 바디를 직접 작성할 수 없기 때문이었다.
Map도 같은 이유이다.
① Map은 인터페이스다.
② 따라서 Body를 직접 작성할 수 없다.
③ HashMap은 Map 인터페이스를 구현했다.
2. 코드의 유연성을 높일 수 있으니까
▶ Map을 구현한 객체는 HashMap, TreeMap 등이 있다.
Map으로 선언할 경우, HashMap으로 선언할 때보다 가짓수가 많아진다.
결국 코드의 유연성을 높일 수 있게 되는 것이다.
① Map map = new TreeMap<>();
② Map map = new HashMap<>();
Map 데이터 삽입
map.put("animal", "cat"); // {animal=cat}
map.put("food", "pizza"); // {animal=cat, food=pizza}
// 이미 "animal" 키값이 있기 때문에 "dog" 로 갱신되지 않음
map.putIfAbsent("animal", "dog"); // {animal=cat, food=pizza}
map.putIfAbsent("animal2", "dog"); // {animal=cat, animal2=dog, food=pizza}
Map<String, String> cities = new HashMap<>();
cities.put("Tokyo", "Japan");
cities.put("Seoul", "Korea");
cities.put("Beijing", "China");
cities.put("Paris", "France");
cities.put("Washington", "USA");
cities.put("Brazilia", "Brazil");
V put(K key, V value) 로 값을 넣을 수 있다.
put(k, v) 을 했을 때 이미 키값이 존재한다면 데이터를 덮어쓴다.
putIfAbsent 을 이용하면 Map 에 Key 값이 없을 때에만 데이터를 넣을 수 있다.
Map 데이터 출력
map.get("animal"); // "cat"
map.getOrDefault("food", "chicken"); // "pizza"
map.getOrDefault("food2", "chicken"); // "chicken"
// Normal ways
//1)
for (Map.Entry<String, String> entry : cities.entrySet()) {
System.out.println("Cities: " + entry.getKey() + ", " + entry.getValue());
}
//2)
for (String key : cities.keySet()) {
System.out.println("Cities: " + key + ", " + cities.get(key));
}
// Java8 forEach, Lambda
// 1)
cities.forEach((k, v) -> System.out.println("Cities: " + k + ", " + v));
// 2)
cities.forEach((k, v) -> {
System.out.println("Cities: " + k + ", " + v);
});
//출력결과
Cities: Beijing, China
Cities: Tokyo, Japan
Cities: Seoul, Korea
Cities: Brazilia, Brazil
Cities: Paris, France
Cities: Washington, USA
V get(Object key) 로 value 값을 가져올 수 있다.
V getOrDefault(Object key, V defaultValue) 을 사용하면 key 값이 없을 때 null 대신 설정된 값을 리턴한다.
Map 데이터 삭제
map.remove("animal2");
V remove(Object key) 로 데이터를 삭제 할 수 있다.
Map 데이터 확인
map.containsKey("food"); // true
map.containsValue("dog"); // false
boolean containsKey(Object key) 또는 boolean containsValue(Object value) 로 key 나 value 값이 존재하는 지 확인할 수 있다.
Map 크기(길이) 확인
map.size();
Key, Value 묶음 가져오기
// map: {animal=cat, food=pizza}
map.keySet(); // [animal, food]
map.values(); // [cat, pizza]
Set<K> keySet() 은 Key들로 이루어진 Set 자료구조를 리턴한다.
Collection<V> values() 은 Value 들로 이루어진 Collection 을 리턴한다.
Map 순회
// 출력
// animal: cat
// food: pizza
map.forEach((k, v) -> {
System.out.println(k + ": " + v);
});
// 한 줄이면 중괄호 { } 생략 가능
map.forEach((k, v) -> System.out.println(k + ": " + v));
// 람다식 안쓰고 for 문으로 구현. forEach 도 내부적으로는 이렇게 구현되어있음
for (Map.Entry entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
forEach 를 사용해서 Map 을 순회할 수 있다.
람다식을 이용하면 좀더 간단하게 나타낼 수 있으며 코드가 한줄이라면 중괄호 { } 를 생략할 수 있다.
Map Compute 데이터 치환
map.compute("animal", (k, v) -> {
System.out.println(k + "'s value is " + v + " -> lion"); // animal's value is cat -> lion
return "lion";
});
// map: {animal=lion, food=pizza}
map.computeIfAbsent("fruit", (k) -> {
System.out.println("New value of " + k + " is apple"); // New value of fruit is apple
return "apple";
});
// map: {fruit=apple, animal=lion, food=pizza}
map.computeIfPresent("animal", (k, v) -> {
System.out.println(k + "'s value is " + v + " -> tiger"); // animal's value is lion -> tiger
return "tiger";
});
// map: {fruit=apple, animal=tiger, food=pizza}
compute 를 사용해 원하는 로직을 실행하고 데이터를 넣을 수 있다.
만약 Key 가 없으면 새로운 데이터를 넣어주고 Key 값이 있으면 데이터를 갱신해준다.
만약 기존에 없는 데이터로 compute 연산을 하게 될 때 value 값은 null 이 된다.
computeIfAbsent 와 computeIfPresent 는 조건을 걸어서 compute 연산을 실행한다.
computeIfAbsent 는 Key 가 없을 때만 실행되기 때문에 람다식으로도 key 값 하나 밖에 받지 않는다.
computeIfPresent 는 Key 값이 존재할 때만 실행된다.
만약 Key 가 없거나 있어서 조건이 일치하지 않으면 로직이 아예 실행되지 않는다.
LinkedHashMap 순서 유지
Map<String, String> map = new LinkedHashMap<>();
map.put("animal", "cat");
map.put("fruit", "apple");
System.out.println(map); // {animal=cat, fruit=apple}
map.put("animal", "dog");
System.out.println(map); // {animal=dog, fruit=apple}
map.forEach((k, v) -> System.out.print(k + ": " + v + ", ")); // animal: dog, fruit: apple,
HashMap 은 hashcode 를 사용하기 때문에 순서가 일정하지 않다.
LinkedHashMap 은 내부를 Double-Linked List 로 구성하여 HashMap 의 순서를 유지한다.
HashMap 에서 상속받기 때문에 HashMap 의 모든 메소드를 사용할 수 있다.
데이터는 먼저 들어간 데이터가 무조건 앞에 위치하게 된다.
forEach 문에서도 동일하다.
추가 소스
package exam;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
public class exam3 {
public static void main(String[] args) {
Map<String, String> map = new LinkedHashMap<String, String>();
map.put("key01", "value01");
map.put("key02", "value02");
map.put("key03", "value03");
map.put("key04", "value04");
map.put("key05", "value05");
//entrySet() 메서드는 key와 value의 값 모두 출력
//keySet() 메서드는 key의 값만 출력
//Iterator는 자바의 컬렉션 프레임워크에서 컬렉션에 저장되어 있는 요소들을 읽어오는 방법
// 방법 01 : entrySet()
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println("방법1: [key]:" + entry.getKey() + ", [value]:" + entry.getValue());
}
System.out.println("----------------------------------------------------------------------------------");
// 방법 02 : keySet()
for (String key : map.keySet()) {
String value = map.get(key);
System.out.println("방법2: [key]:" + key + ", [value]:" + value);
}
System.out.println("----------------------------------------------------------------------------------");
// 방법 03 : entrySet().iterator()
Iterator<Map.Entry<String, String>> iteratorE = map.entrySet().iterator();
while (iteratorE.hasNext()) {
Map.Entry<String, String> entry = (Map.Entry<String, String>) iteratorE.next();
String key = entry.getKey();
String value = entry.getValue();
System.out.println("방법3: [key]:" + key + ", [value]:" + value);
}
System.out.println("----------------------------------------------------------------------------------");
// 방법 04 : keySet().iterator()
Iterator<String> iteratorK = map.keySet().iterator();
while (iteratorK.hasNext()) {
String key = iteratorK.next();
String value = map.get(key);
System.out.println("방법4: [key]:" + key + ", [value]:" + value);
}
System.out.println("----------------------------------------------------------------------------------");
// 방법 05 : Lambda 사용
map.entrySet().stream().forEach(entry-> {
System.out.println("방법5: [key]:" + entry.getKey() + ", [value]:"+entry.getValue());
});
System.out.println("----------------------------------------------------------------------------------");
// 방법 06 : Stream 사용
map.entrySet().stream().forEach(entry-> {
System.out.println("방법6-1: [key]:" + entry.getKey() + ", [value]:"+entry.getValue());
});
System.out.println("----------------------------------------------------------------------------------");
// Stream 사용 - 내림차순
map.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(entry-> {
System.out.println("방법6-2: [key]:" + entry.getKey() + ", [value]:"+entry.getValue());
});
System.out.println("----------------------------------------------------------------------------------");
// Stream 사용 - 오름차순
map.entrySet().stream().sorted(Map.Entry.comparingByKey(Comparator.reverseOrder())).forEach(entry-> {
System.out.println("방법6-3: [key]:" + entry.getKey() + ", [value]:"+entry.getValue());
});
}
}