Core Java (二十一) 映射表(Map介面)

來源:互聯網
上載者:User

映射表是一種資料結構,用於存放索引值對。如果提供了鍵,就能尋找到值。

Map介面的方法:

Modifier and Type Method and Description
void clear()

Removes all of the mappings from this map (optional operation).
boolean containsKey(Object key)

Returns true if this map contains a mapping for the specified key.
boolean containsValue(Object value)

Returns true if this map maps one or more keys to the specified value.
Set<Map.Entry<K,V>> entrySet()

Returns a Set view
of the mappings contained in this map.
boolean equals(Object o)

Compares the specified object with this map for equality.
V get(Object key)

Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.
int hashCode()

Returns the hash code value for this map.
boolean isEmpty()

Returns true if this map contains no key-value mappings.
Set<K> keySet()

Returns a Set view
of the keys contained in this map.
V put(K key, V value)

Associates the specified value with the specified key in this map (optional operation).
void putAll(Map<?
extends K,? extends V> m)

Copies all of the mappings from the specified map to this map (optional operation).
V remove(Object key)

Removes the mapping for a key from this map if it is present (optional operation).
int size()

Returns the number of key-value mappings in this map.
Collection<V> values()

Returns a Collection view
of the values contained in this map.

Java類庫為映射表提供了兩個通用的實現:HashMap(對鍵進行散列)和TreeMap(用鍵的整體順序對元素排序,並將其組織成搜尋樹),這兩個類都實現了Map介面。

HashMap比TreeMap更快,但沒有排序功能。下面重點介紹HashMap:

HashMap

散列表(HashMap)和散列集(HashSet)都是散列表這種資料結構。

散列表(Hash Table)

散列表由鏈表數組實現,每個列表成為一個桶(bucket),一個桶就是一個鏈表。如所示,該圖有16個桶。根據關鍵字的散列碼(HashCode)與桶的總數取餘得到元素的位置。圖中,0~15代表桶的標號,而右邊一些數字代表關鍵字的散列碼。下面這個散列表,其中4個空間被使用,即36%的空間已經被使用。而散列表的裝填因子(load factor)預設為0.75,即當75%的空間已經被使用時進行再散列(rehashed),桶的總數變成雙倍。注意:如果未設定,桶的總數的初始預設值為16。

這裡主要討論HashMap:

散列映射表(HashMap)僅僅是根據關鍵字的散列碼(HashCode)來得到元素要存放在哪條鏈表上,而判斷兩個對象是否相等是根據對象的equals方法確定的。如果想將Employee類的某個對象添加到HashMap中,首先要調用這個對象(Value)所對應的鍵(Key)的hashCode方法計算鍵的散列碼,然後與桶的總數取餘的到這個索引值對的桶的索引。如果返回的散列碼與HashMap中已經存在的某個鍵的散列碼相同,則第二個值就會取代第一個值。因為鍵必須是唯一的,不能對同一個鍵存放兩個值。也就是說,散列映射表中,value完全是key的附屬,一點也不會影響儲存。

HashMap儲存在bucket中的是entry(條目)的引用,一個bucket其實就是一條entry鏈表。entry是一種資料結構,裡麵包含了key,value,next等執行個體域,其中next指的是下一個引用的位置,如果next == null,則表示這條entry鏈結束了。HashMap在執行add操作的時候,總是把最新的元素添加到bucket中entry鏈表的頭部,再將這個最新元素的next指向舊鏈表,所以最早加入的元素一定在鏈表末尾。

其他的知識一定要讀一讀這篇文章:java中HashMap詳解

這裡附加一道OCJP的題目來增強理解:

Given

public class Person { private name; public Person(String name) { this.name = name; } public int hashCode() { return 420; } }

Which statement is true?
A. The time to find the value from HashMap with a Person key depends on the
size of the map.
B. Deleting a Person key from a HashMap will delete all map entries for all keys of type Person.
C. Inserting a second Person object into a HashSet will cause the first Person object to be
removed as a duplicate.
D. The time to determine whether a Person object is contained in a HashSet is constant and does NOT
depend on the size of the map.
Answer: A

下面來解釋一下:

紅色為翻譯

B選項:刪除HashMap中一個Person對象對應的鍵將會刪除這個散列映射表中Person類的全部條目。錯誤,HashMap中Person對象的索引值不是由Person對象決定的,而是程式給定的鍵,例如staff.add("123-345", bob),就是把鍵為123-456的bob對象添加到名為staff的HashMap中,因而HashMap允許添加相同的對象。所以說,刪除一個鍵對應的Person對象並不會刪除所有的條目,他們的key都不同嘛。

C選項:向HashSet中插入另外一個Person對象將會引起第二個對象覆蓋第一個對象。錯誤,雖然Person對象的hashCode方法返回的值都是420,這僅僅表明兩個Person對象在一個entry鏈表中,接下來要調用equals方法,由於Person類沒有equals方法,所以調用Object的equals方法返回對象的儲存地址,很明顯兩個Person對象的儲存地址是不同的。綜上,HashSet中可以添加不同的Person對象,只要equals方法傳回值不同就好。

D選項:判斷一個HashSet中是否存在一個Person對象的次數是常數次,和map的大小無關。錯誤,由於Person對象的hashCode返回的值都是420,所以HashSet中的Person對象都在一個bucket中,組成了一條entry鏈表,查詢速度與entry鏈表的大小息息相關。

A:選項:由key來尋找value的次數與map的大小有關。正確,map越大,即bucket的個數越多,entry鏈的長度相應的來說就越小(hashcode和桶個數取餘後的數一樣的幾率就越小)。

另外,要注意的是HashMap有三個重要的視圖:

  • Set<K> keySet();
  • Collection<K> values();
  • Set<Map.Entry<K,V>> entrySet();
其中第一個方法和第三個方法返回的是一個集(Set),而第二個方法返回的是一個集合(Collection)。

以下例子利用了HashMap的一些重要的方法:

package com.xujin;import java.util.HashMap;import java.util.Map;import java.util.Set;public class Test{public static void main(String...arg){Map<String, Employee> staff = new HashMap<String, Employee>();Employee lily = new Employee("Lily", 4000);Employee john = new Employee("John", 6000);staff.put("1234-9876", lily);staff.put("345-8765", john);Employee e = staff.get("345-8765");System.out.println(e.getName());//JohnSystem.out.println(staff.containsKey("1234-9876"));//trueSystem.out.println(staff.containsValue(lily));//trueSystem.out.println(staff.size());//2//鍵集Set<String> myKeySet =  staff.keySet();for(String myKey: myKeySet){System.out.println(myKey);}/*結果: * 345-8765 * 1234-9876*///值集合(不是集set)//staff.values()傳回型別為Collection<Employee>for(Employee myEmployee : staff.values()){System.out.println(myEmployee);}/*結果: * id:2,name:John,salary:6000.0 * id:1,name:Lily,salary:4000.0 * *///索引值對集//staff.entrySet()傳回型別為Set<Entry<String, Employee>>for(Map.Entry<String, Employee> entry: staff.entrySet()){String key = entry.getKey();Employee value = entry.getValue();System.out.println("key:  " + key + "\nvalue:  " + value);}/*結果: * key:  345-8765 * value:  id:2,name:John,salary:6000.0 * key:  1234-9876 * value:  id:1,name:Lily,salary:4000.0 * */staff.remove("345-8765");System.out.println(staff.size());//1//putAll方法,Map<String, Manager> otherStaff = new HashMap<String, Manager>();Manager jim = new Manager("Jim", 6000, 1000);Manager gin = new Manager("Gin", 8000, 2000);otherStaff.put("1111-2222", jim);otherStaff.put("2222-3333", gin);staff.putAll(otherStaff);for(Map.Entry<String, Employee> entry: staff.entrySet()){String key = entry.getKey();Employee value = entry.getValue();System.out.println("key:  " + key + "\nvalue:  " + value);}/*結果: * key:  1111-2222 * value:  id:3,name:Jim,salary:6000.0,bonus:1000.0 * key:  2222-3333 * value:  id:4,name:Gin,salary:8000.0,bonus:2000.0 * key:  1234-9876 * value:  id:1,name:Lily,salary:4000.0 * */}}class Employee{public Employee(String name){this.name = name;id = nextId;nextId++;}public String toString(){return "id:" + id + ",name:" + name +",salary:" + salary;}public Employee(String name, double salary){this(name);//調用另一構造器this.salary = salary;}//定義訪問器方法public final String getName(){return name;}public double getSalary(){return salary;}public final int getId(){return id;}//定義更改器方法public final void setName(String name){this.name = name;}public final void setSalary(double salary){this.salary = salary;}public final void raiseSalary(double percent){this.salary *= (1 + percent);}//定義變數private String name = "";//執行個體域初始化private double salary;private int id;private static int nextId = 1;}class Manager extends Employee{public Manager(String name, double salary, double bonus){super(name, salary);//super在構造器中的使用,可以調用超類的構造器setBonus(bonus);}public String toString(){return super.toString() + ",bonus:" + bonus;}public double getBonus(){return bonus;}//重寫getSalary方法public double getSalary(){double baseSalary = super.getSalary();//調用了超類的getSalary方法return baseSalary + bonus;}public void setBonus(double bonus){this.bonus = bonus;}private double bonus;}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.