컬렉션 프레임워크2: Map계열 자세히, 예시

컬렉션 프레임워크2: Map계열 자세히, 예시

Collections 프레임워크




Map계열

https://lelumiere.tistory.com/3

  • Map<K,V>는 컬렉션 프레임워크에 포함되어있지만 Collections을 상속받지 않는다.
  • 따라서 Collections 메서드를 사용할 수없고 고유의 메서드가 있다.

https://palpit.tistory.com/656

  • Map, table 접미어가 붙은 자료구조이다.
  • Map<K,V>는 키와 벨류를 의미하며 데이터저장시 (키, 데이터)쌍으로 저장하여 사용하는 구조
    • key 중복 불가
    • value 중복 가능
    • Entity를 추가할때 key가 중복디면 기존값을 덮어써버린다. 즉 추가와 수정메서드가 동일하다
    • Entry<K,V>타입은 key와 value를 쌍으로 가진 타입이다.
  • map은 인터페이스이므로 객체생성을 할수없지만 구현을 통해서 생성가능하다
    • 가장 많이 사용하는 구현체 : HashMap<K,V>
    • Hashtable<K,V> 클래스 : 맵인터페이스를 구현한 클래스. 멀티스레드에 안전하지만 성능은 좋지 않은 과거버전이다. Hashtable<K,V>보다 HashMap<K,V>을 사용하자
  • 장점 : 키값을 사용해서 검색 인덱스생성 -> 데이터 검색시간이 짧음.




HashMap<K,V> 전화번호 관리 어플리케이션 예시

  • HashMap<K,V> 특징
    • 순서가 없음
    • default정렬기준은 키값
  • 아래 예시는 가장 많이 사용하는 방식으로 꼭 암기할 것.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class HashMapTest {
Map<String, String> hMap = new HashMap<>();

private void addMethod(){
System.out.println("추가 결과 확인: "+hMap.put("강대표", "1234"));
System.out.println("추가 결과 확인: "+hMap.put("서회장", "4567"));
hMap.put("하지", "4567");
hMap.put("우나", "9999");
}

private void retreveMethod(){
System.out.println("총 entity 개수: "+hMap.size());
Set<String> keys = hMap.keySet();
for(String key : keys){
System.out.println("키: "+key+"값: "+hMap.get(key));
}
String targetNumber = "4567";
Set<Map.Entry<String, String>> entrySet = hMap.entrySet();
for(Map.Entry<String, String> entry : entrySet){
if(entry.getValue().equals(targetNumber)){
System.out.println("번호가 "+targetNumber+"인 사람: "+entry.getKey());
}
}
System.out.println("휘슬의 전화번호가 있는가? "+hMap.containsKey("휘슬"));
}

private void removeMethod(){
hMap.remove("하지");
System.out.println(hMap);
}

public static void main(String[] args) {
HashMapTest hmt = new HashMapTest();
hmt.addMethod();
System.out.println(hmt);
hmt.retreveMethod();
hmt.removeMethod();
}

}
//출력값
추가 결과 확인: null
추가 결과 확인: null
총 entity 개수: 4
키: 우나, 값: 9999
키: 하지, 값: 4567
키: 서회장, 값: 4567
키: 강대표, 값: 1234
번호가 4567인 사람: 하지
번호가 4567인 사람: 서회장
휘슬의 전화번호가 있는가? false
{우나=9999, 서회장=4567, 강대표=1234}




TreeMap<K,V> 개념과 그룹화와 정렬예시

  • Map계열에도 Set계열처럼 정렬이 지원되는 TreeMap<K,V>가 존재한다.
  • Key값을 이용해 내부에 저장된 Entry들을 정렬시킨다.
  • 정렬방식 : TreeSet과 동일.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class TreeMapTest {
private static Map<String, String> initMap(Map<String, String> map){
map.put("떡볶이", "4000원");
map.put("순대", "4000원");
map.put("만두튀김", "500원");
map.put("야채튀김", "500원");
return map;
}

public static void main(String[] args) {
Map<String, String> map = initMap(new HashMap<>());
System.out.println("HashMap의 정렬 상태1: "+map);
Map<String, String> tmap = initMap(new TreeMap<>());
System.out.println("TreeMap의 정렬 상태2: "+tmap);
if(tmap instanceof NavigableMap<?, ?>){
NavigableMap<String, String> navMap = (NavigableMap<String, String>) tmap;
System.out.println("Nav맵의 맨 처음 요소: "+navMap.firstEntry());
System.out.println("Nav맵의 내림차순 정렬: "+navMap.descendingMap());
}

Map<String, String> tmap2 = initMap(new TreeMap<>(new StringLengthComparator()));
System.out.println("TreeMap글자길이 정렬 상태3: "+tmap2);
}
}
//출력값
HashMap의 정렬 상태1: {만두튀김=500원, 야채튀김=500원, 순대=4000원, 떡볶이=4000원}
TreeMap의 정렬 상태2: {떡볶이=4000원, 만두튀김=500원, 순대=4000원, 야채튀김=500원}
Nav맵의 맨 처음 요소: 떡볶이=4000
Nav맵의 내림차순 정렬: {야채튀김=500원, 순대=4000원, 만두튀김=500원, 떡볶이=4000원}
맵의 정렬 상태3: {순대=4000원, 떡볶이=4000원, 만두튀김=500원}




HashMap<K,V> 로또게임 예시

  • 고급스러운 예시를 보자.

  • 100만 번 랜덤한 숫자를 발생시킨 후 뽑힌번호를 키로, 빈도수를 값으로 Map<K,V>에 저장해놓고 빈도수를 기준으로 상위1~6번까지 뽑힌번호를 당첨번호, 보너스는 7번째번호를 출력하는 로또게임 예시

  • compare() : Entry<K,v>를 정렬하기 위해 Comparator<>를 사용.

    • 1차 정렬 : 값을 사용하고 내림차순을 위해 -1 곱함
    • 2차 정렬 : 만약 1차정렬에서 동일한 값이 나온 경우 key를 이용하고 내림차순을 위해 -1 곱함.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
class LottoEntryComparator implements Comparator<Entry<Integer, Integer>> {

@Override
public int compare(Entry<Integer, Integer> arg0, Entry<Integer, Integer> arg1) {
int result = arg0.getValue().compareTo(arg1.getValue()) * -1;
if(result == 0){ //1차정렬값이 동일한 경우
result = arg0.getKey().compareTo(arg1.getKey()) * -1;
}
return result;
}
}


public class MapLottoTest {

public static void main(String[] args) {
Map<Integer, Integer> lottoMap = new HashMap<>();
Random rd = new Random();
for(int i=0; i<100000; i++){
int num = rd.nextInt(45)+1; //1부터 46까지의 숫자
if(lottoMap.containsKey(num)){
lottoMap.put(num, lottoMap.get(num)+1);
}else{
lottoMap.put(num, 1);
}
}
sortLottoByV(lottoMap);
}

public static void sortLottoByV(Map<Integer, Integer> map){
TreeSet<Map.Entry<Integer, Integer>> sortedEntries = new TreeSet<>(new LottoEntryComparator());
sortedEntries.addAll(map.entrySet());
String pattern = "{0}번호: {1}\t(총 누적:{2})";
for (int i=0; i<7; i++){
Entry<Integer, Integer> first = sortedEntries.pollFirst();
String msg = null;
if(i == 6){
msg = MessageFormat.format(pattern, "보너스", first.getKey(), first.getValue());
}else{
msg = MessageFormat.format(pattern, "당첨", first.getKey(), first.getValue());

}
System.out.println(msg);
}
}

}
//출력값
당첨번호: 38 (총 누적:2,336)
당첨번호: 1 (총 누적:2,328)
당첨번호: 5 (총 누적:2,321)
당첨번호: 28 (총 누적:2,316)
당첨번호: 29 (총 누적:2,292)
당첨번호: 27 (총 누적:2,283)
보너스번호: 45 (총 누적:2,268)




Properties<K,V>

  • 어플리케이션의 환경 설정 파일에 데이터를 읽고 쓰는 기능
  • 일반적으로 확장자를 .properties로 작성.
  • Entry형태로 값을 여러개 저장 가능.
  • 예제 : 속성저장하고 읽기.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class PropertiesTestWithFile {
private final static String PROP_FILE_PATH="./config.properties";

private static void saveToFile(){
Properties props = new Properties();
props.setProperty("id", "joji_K");
props.setProperty("pw", "1234");
props.setProperty("addr", "192.168.0.2");
props.setProperty("이름", "민지");
System.out.println("속성확인: "+props);

try(FileWriter output = new FileWriter(PROP_FILE_PATH)){
props.store(output, "System Config");
}catch(Exception e){
e.printStackTrace();
}
}

private static void loadFromFile(){
Properties props = new Properties();
try(FileReader input = new FileReader(PROP_FILE_PATH)){
props.load(input);
Set<String> keys = props.stringPropertyNames();
for(String key:keys){
System.out.println(key+" : "+props.getProperty(key));
}
}catch(Exception e){
e.printStackTrace();
}
}

public static void main(String[] args) {
saveToFile();
loadFromFile();
}

}
//출력값
속성확인: {이름=민지, pw=1234, addr=192.168.0.2, id=joji_K}
이름 : 민지
pw : 1234
addr : 192.168.0.2
id : joji_K
  • 동일한 프로젝트안에 config.properties파일명으로 properties파일이 생성된 것을 확인할 수 있다.