映射

映射 在 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取決於name欄位,而這個欄位可以透過setName改變。 改變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 個練習,就能練習 映射