[TIL | 내일배움캠프] Map

KnitDev - BCJ·2025년 10월 15일

Java

목록 보기
8/8

코딩테스트

(25. 10. 15)
어려운 문제 도전했다가 식겁했다.
2019년 카카오톡 코테문제.

입력받은 문자열 배열을 userid로 구분해 닉네임의 변경 정보를 반영해서 메시지 유형에 맞게 출력하도록 하는 문제였다.
상세 문제 내용은 프로그래머스 페이지 참조

// 클래스 객체를 활용해 User 정보를 저장하고 변경할 수 있도록 시도
import java.util.ArrayList;
import java.util.List;
// List로 User 정보를 담기
class Solution {
    public String[] solution(String[] record) {
        //record에서 가져온 유저 목록
        List<UserInfo> userList = new ArrayList<>();
        //결과 메시지를 담을 문자열 목록
        List<String> match = new ArrayList<>();
//유저 정보를 userList에 등록하고 닉네임 변경사항을 저장하도록 한다. 
        for (String s : record) {
  //for문으로 입력받은 문자열("Enter uid1234 Muzi")를 배열로 만든다.
  // sentence : [Enter, uid1234, Muzi]
            String[] sentence = s.split(" ");
  //"Leave"가 메시지 내용일 경우 내용 변경이 필요없으므로 if문으로 skip했다.
            if (sentence[0].equals("Enter") || sentence[0].equals("Change")) {
                UserInfo user = new UserInfo(sentence[1], sentence[2]);
  //유저 목록이 빈 경우 + 등록되지 않은 userId인 경우 새 유저 정보 등록
                if (userList.isEmpty() || !(userList.contains(user))) {
                    userList.add(user);
                    continue;
                }
  //이미 유저 목록에 있는 유저 정보일 경우 닉네임 정보를 현행화한다. 
                for (int i = 0; i < userList.size(); i++) {
                    if(userList.get(i).getUserId().equals(user.getUserId())){
                        userList.get(i).setNickName(sentence[2]);
                        break;
                    }
                }
            }
        }
//유저 목록 갱신이 종료되었으므로 각 동작에 맞는 메시지를 출력하도록 결과 List에 저장한다.
        for (String string : record) {
            String[] s = string.split(" ");
            for (UserInfo userInfo : userList) {
                if (!(s[0].equals("Change")) && userInfo.getUserId().equals(s[1])) {
                    match.add(userInfo.printResult(s[0]));
                    break;
                }
            }
        }
//결과 List match를 문자열 배열 answer에 담는다. 
        String[] answer = match.toArray(new String[match.size()]);
        return answer;
    }
}```
  class UserInfo {
  //1. 속성
        private String userId;
        private String nickName;
  //2. 생성자
    public UserInfo(String s, String s1) {
        this.userId = s;
        this.nickName = s1;
    }
  //3. 메서드 정의(Getter/Setter)
    public String getUserId() {
            return this.userId;
        }
        public void setUserId(String id) {
            this.userId = id;
        }
        public String getNickName() {
            return this.nickName;
        }
        public void setNickName(String name) {
            this.nickName = name;
        }
  //input 값으로 출력할 메시지 내용을 구분해 문자열 형태로 반환해주는 메서드
        public String printResult(String input) {
            String stn = input.equals("Enter") ? "님이 들어왔습니다." : "님이 나갔습니다.";
            return this.nickName + stn;
        }
  //contains() 메서드의 매개변수로 UserInfo 클래스를 넣을 수 있도록 Override
  //userId로 포함 여부를 구분하도록 함
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        UserInfo user = (UserInfo) obj;
        return userId.equals(user.userId);
    }
    @Override
    public int hashCode() {
        return userId.hashCode();
    }
}

보다시피 결과는 제대로 나오는데 1)for문과 if문이 많고 2)같은 배열을 2번 만들어내고 있고 3)Class를 선언하는 터라 테스트 케이스 26?쯤에서 시간 초과로 합격하지 못했다. 78점...

다른 사람들은 어떻게 했나 살펴보니 Map을 사용했길래 다시 복습하기로 했다.

Map

내가 자주 사용하는 List와 같은 Collection 인터페이스와 달리 키-값(key-value)을 매핑한 방식으로 요소를 가진다. 그래서 Map 또 다른 차이점으로 저장 순서가 유지되지 않는다.
key라는 이름에서 유추할 수 있듯이 값을 얻으려면 key를 통해서 얻어야 한다. 그리고 key로 value를 구분하기 때문에 중복된 key값은 허용되지 않는다. (value는 중복OK, null도 OK)

HashMap

  public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {
      ...
  //선언할 때는 아래와 같이 키-값의 자료형을 넣어주면 된다. 
  HashMap<String, String> userMap = new HashMap<>();
  //그러나 보통 List<String> list = new ArrayList<>(); 처럼 선언하듯이
  Map<String, String> userMap = new HashMap<>();
  //HashMap도 이렇게 선언하면 된다. 

LinkedHashMap

HashMap 앞에 Linked가 붙었다.
Linked 하면 연상되는 것은 당연히 List. LinkedHashMap은 입력된 순서대로 데이터를 출력해주는 HashMap이다.

TreeMap

LinkedHashMap과는 조금 다르게 순서 기준이 정렬된 key 기준으로 데이터를 출력해준다. 왜 Tree가 붙었을까, 궁금해서 검색해봤는데 이진탐색 트리 구조에서 발전된 Red-Black Tree 구조를 이용하고 있어서라는 걸 알게 되었다. 상세히 읽고싶다면 참조

Method

Method동작반환 자료형
put(K, V)K(key)에 대응하는 V(value)를 매핑해 담아준다.V
remove(K)K(key)에 대응하는 매핑을 제거한다.V
remove(K, V)V에 대응하는 K의 매핑을 제거한다.boolean
clear()Map의 모든 매핑을 제거한다.void
containsKey(K)해당 Map이 K를 포함하고 있는지 확인한다.boolean
containsValue(V)해당 Map이 하나 이상의 V(value)를 포함하는지 확인한다.boolean
replace(K, V)K(key)에 대응하는 값을 V(value)로 바꿔준다.V
replace(K, V(old), V(new))K(key)에 대응하는 V(old)를 새로운 V(new)로 바꿔준다.boolean
isEmpty()해당 맵이 비어있는지 확인한다.boolean
size()해당 Map의 총 개수를 반환한다.int

활용

그래서 HashMap으로 어떻게 위 문제를 활용하느냐...User 정보를 id와 nickname으로 묶어 Map 형태로 담아줬다.

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
//추가 클래스 선언없이 Solution 하나로 끝나는 코드 
class Solution {
    public String[] solution(String[] record) {
        Map<String, String> userMap = new HashMap<>();
        List<String[]> logs = new ArrayList<>();
//문자열 분리는 for문 그대로 | 그러나!! 맨 처음 한번만 수행
        for (String entry : record) {
            String[] parts = entry.split(" ");
            String action = parts[0];
            String userId = parts[1];
//메시지 유형에 따라 출력될 문자열 배열 리스트를 만드는 동작을 분리
            if (action.equals("Enter")) {
                userMap.put(userId, parts[2]);
                logs.add(new String[]{userId, "Enter"});
            } else if (action.equals("Leave")) {
                logs.add(new String[]{userId, "Leave"});
            } else if (action.equals("Change")) {
                userMap.put(userId, parts[2]); // 닉네임 변경
            }
        }
//결과 문자열을 answer에 각각 id, nickname에 맞게 넣어준다. 
        String[] answer = new String[logs.size()];
        for (int i = 0; i < logs.size(); i++) {
            String userId = logs.get(i)[0];
            String action = logs.get(i)[1];
            String nickname = userMap.get(userId);
            String message = nickname + (action.equals("Enter") ? "님이 들어왔습니다." : "님이 나갔습니다.");
            answer[i] = message;
        }
        return answer;
    }
}

코드가 훨씬 깔끔해졌고 다른 클래스를 선언하지 않아도 되어서 시간 초과없이 문제를 통과할 수 있었다. 역시 아는 게 힘!

profile
우당탕탕얼레벌레 개발 일지와 일상

0개의 댓글