맵

맵 에서 Java

11개의 연습 문제

맵 소개

Map은 키와 값을 짝지어 저장하는 자료 구조예요. 다른 프로그래밍 언어의 딕셔너리와 비슷해요. Map 인터페이스는 Map으로 할 수 있는 연산을 정의해요.

HashMap

Java에는 여러 가지 Map 구현이 있어요. HashMap은 그중에서도 자주 사용하는 구현이에요.

// Make an instance
Map<String, Integer> fruitPrices = new HashMap<>();
Note

Map 변수를 선언할 때는 위 예시처럼 구체적인 타입이 아니라 Map 타입으로 선언하는 것이 좋아요. 이렇게 해두면 나중에 Map 구현을 바꾸기 쉬워요.

HashMap에는 복사 생성자도 있어요.

// Make a copy of a map
Map<String, Integer> copy = new HashMap<>(fruitPrices);

put으로 Map에 항목을 추가해요.

fruitPrices.put("apple", 100);
fruitPrices.put("pear", 80);
// => { "apple" => 100, "pear" => 80 }

각 키에는 값 하나만 연결할 수 있어요. 같은 키로 put을 호출하면 그 키의 값이 갱신돼요.

fruitPrices.put("pear", 40);
// => { "apple" => 100, "pear" => 40 }

get으로 키에 해당하는 값을 가져와요.

fruitPrices.get("apple"); // => 100

containsKey로 Map에 특정 키가 있는지 확인해요.

fruitPrices.containsKey("apple");   // => true
fruitPrices.containsKey("orange");  // => false

remove로 항목을 제거해요.

fruitPrices.put("plum", 90);  // Add plum to map
fruitPrices.remove("plum");   // Removes plum from map

size 메서드는 항목의 개수를 반환해요.

fruitPrices.size();  // Returns 2

keySet이나 values 메서드를 사용하면 Map의 키와 값을 각각 Set과 컬렉션으로 얻을 수 있어요.

fruitPrices.keySet();  // Returns "apple" and "pear" in a set
fruitPrices.values();  // Returns 100 and 80, in a Collection

HashMap은 hashCode와 equals를 사용해요

HashMap은 객체의 hashCode와 equals 메서드를 이용해서 키의 값을 어디에 저장하고 어떻게 가져올지 결정해요. 그래서 이 메서드들의 반환 값이 값을 저장할 때와 가져올 때 달라지지 않는 것이 중요해요. 그렇지 않으면 HashMap이 값을 찾지 못할 수도 있어요.

예를 들어, Map의 키로 사용할 다음 클래스가 있다고 해봐요.

public class Stock {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (Objects.equals(Stock.class, obj.getClass()) && obj instanceof Stock other) {
            return Objects.equals(name, other.name);
        }
        return false;
    }
}

hashCode와 equals는 setName으로 바꿀 수 있는 name 필드에 의존해요. hashCode를 바꾸면 예상치 못한 결과가 나올 수 있어요.

Stock stock = new Stock();
stock.setName("Beanies");

Map<Stock, Integer> stockCount = new HashMap<>();
stockCount.put(stock, 80);

stockCount.get(stock); // Returns 80

Stock other = new Stock();
other.setName("Beanies");

stockCount.get(other); // Returns 80 because "other" and "stock" are equal

stock.setName("Choccies");
stockCount.get(stock); // Returns null because hashCode value has changed

stockCount.get(other);  // Also returns null because "other" and "stock" are not equal

stock.setName("Beanies");
stockCount.get(stock); // HashCode restored, so returns 80 again

stockCount.get(other); // Also returns 80 again because "other" and "stock" are back to equal

Map.of와 Map.copyOf

Map을 만드는 또 다른 흔한 방법은 Map.of나 Map.ofEntries를 사용하는 거예요.

// Using Map.of
Map<String, Integer> temperatures = Map.of("Mon", 30, "Tue", 28, "Wed", 32);

// or using Map.ofEntries
Map<String, Integer> temperatures2 = Map.ofEntries(
    Map.entry("Mon", 30),
    Map.entry("Tue", 28),
    Map.entry("Wed", 32)
);

HashMap과 달리 이 메서드들은 Map을 미리 채워 두고, 한 번 만들면 읽기 전용이 돼요. Map.copyOf는 Map의 읽기 전용 복사본을 만들어요.

Map<String, Integer> readOnlyFruitPrices = Map.copyOf(fruitPrices);

put, remove, clear 같은 메서드를 호출하면 UnsupportedOperationException이 발생해요.

GitHub에서 편집 링크가 새 창이나 탭에서 열려요

맵 배우기

연습이 잠겨 있어요

맵 개념을 연습하려면 연습 문제 1개를 더 잠금 해제해요