Java学习之HashMap: 如何正确实现Map的entrySet()方法

转载链接:http://410063005.iteye.com/blog/1676845

通过继承AbstractMap我们可以很容易实现自己的Map,我们只需要实现唯一的抽象的entrySet()方法。 以下是来自《Jav编程思想》(第四版第17章的例子),继承AbstractMap实现了自己的SlowMap。 另外还应该注意, 如果要创建自己的Map,还必须同时定义Map.Entry的实现。

 

总结起来实现自定义Map需要以下两个步骤:

  1. 继承AbstractMap需要实现entrySet()方法
  2. 实现自己的Map.Entry

Java代码   收藏代码
  1. //: containers/MapEntry.java  
  2. // A simple Map.Entry for sample Map implementations.  
  3. import java.util.*;  
  4.   
  5. public class MapEntry implements Map.Entry {  
  6.   private K key;  
  7.   private V value;  
  8.   public MapEntry(K key, V value) {  
  9.     this.key = key;  
  10.     this.value = value;  
  11.   }  
  12.   public K getKey() { return key; }  
  13.   public V getValue() { return value; }  
  14.   public V setValue(V v) {  
  15.     V result = value;  
  16.     value = v;  
  17.     return result;  
  18.   }  
  19.   public int hashCode() {  
  20.     return (key==null ? 0 : key.hashCode()) ^  
  21.       (value==null ? 0 : value.hashCode());  
  22.   }  
  23.   public boolean equals(Object o) {  
  24.     if(!(o instanceof MapEntry)) return false;  
  25.     MapEntry me = (MapEntry)o;  
  26.     return  
  27.       (key == null ?  
  28.        me.getKey() == null : key.equals(me.getKey())) &&  
  29.       (value == null ?  
  30.        me.getValue()== null : value.equals(me.getValue()));  
  31.   }  
  32.   public String toString() { return key + "=" + value; }  
  33. ///:~  
  34.   
  35.   
  36.   
  37. //: containers/SlowMap.java  
  38. // A Map implemented with ArrayLists.  
  39. import java.util.*;  
  40. import net.mindview.util.*;  
  41.   
  42. public class SlowMap extends AbstractMap {  
  43.   private List keys = new ArrayList();  
  44.   private List values = new ArrayList();  
  45.   public V put(K key, V value) {  
  46.     V oldValue = get(key); // The old value or null  
  47.     if(!keys.contains(key)) {  
  48.       keys.add(key);  
  49.       values.add(value);  
  50.     } else  
  51.       values.set(keys.indexOf(key), value);  
  52.     return oldValue;  
  53.   }  
  54.   public V get(Object key) { // key is type Object, not K  
  55.     if(!keys.contains(key))  
  56.       return null;  
  57.     return values.get(keys.indexOf(key));  
  58.   }  
  59.   public Set> entrySet() {  
  60.     Set> set= new HashSet>();  
  61.     Iterator ki = keys.iterator();  
  62.     Iterator vi = values.iterator();  
  63.     while(ki.hasNext())  
  64.       set.add(new MapEntry(ki.next(), vi.next()));  
  65.     return set;  
  66.   }  
  67.   public static void main(String[] args) {  
  68.     SlowMap m= new SlowMap();  
  69.     m.putAll(Countries.capitals(15));  
  70.     System.out.println(m);  
  71.     System.out.println(m.get("BULGARIA"));  
  72.     System.out.println(m.entrySet());  
  73.   }  
  74. /* Output: 
  75. {CAMEROON=Yaounde, CHAD=N'djamena, CONGO=Brazzaville, CAPE VERDE=Praia, ALGERIA=Algiers, COMOROS=Moroni, CENTRAL AFRICAN REPUBLIC=Bangui, BOTSWANA=Gaberone, BURUNDI=Bujumbura, BENIN=Porto-Novo, BULGARIA=Sofia, EGYPT=Cairo, ANGOLA=Luanda, BURKINA FASO=Ouagadougou, DJIBOUTI=Dijibouti} 
  76. Sofia 
  77. [CAMEROON=Yaounde, CHAD=N'djamena, CONGO=Brazzaville, CAPE VERDE=Praia, ALGERIA=Algiers, COMOROS=Moroni, CENTRAL AFRICAN REPUBLIC=Bangui, BOTSWANA=Gaberone, BURUNDI=Bujumbura, BENIN=Porto-Novo, BULGARIA=Sofia, EGYPT=Cairo, ANGOLA=Luanda, BURKINA FASO=Ouagadougou, DJIBOUTI=Dijibouti] 
  78. *///:~  

 以上是一个参考实现。这个解决方案看起来很简单并且没什么问题,但这并不是一个恰当的实现。主要问题是它创建了键和值的副本。entrySet()的恰当实现 应该在Map中提供视图,而不是副本,并且这个视图允许对原始的映射表进行修改,而副本则不行。

 

可以参考源码来学习正确的entrySet()应该如何实现。首先先看Map中entrySet()的声明

Java代码   收藏代码
  1. public interface Map {  
  2.     /** 
  3.      * Returns a {@link Set} view of the mappings contained in this map. 
  4.      * The set is backed by the map, so changes to the map are 
  5.      * reflected in the set, and vice-versa.  If the map is modified 
  6.      * while an iteration over the set is in progress (except through 
  7.      * the iterator's own remove operation, or through the 
  8.      * setValue operation on a map entry returned by the 
  9.      * iterator) the results of the iteration are undefined.  The set 
  10.      * supports element removal, which removes the corresponding 
  11.      * mapping from the map, via the Iterator.remove, 
  12.      * Set.removeremoveAllretainAll and 
  13.      * clear operations.  It does not support the 
  14.      * add or addAll operations. 
  15.      * 
  16.      * @return a set view of the mappings contained in this map 
  17.      */  
  18.     Set> entrySet();  
  19. }  
注释文档明确指出,entrySet()返回的Set应该是Map所代表的映射表的一个View。 对Map的修改应该会反映到这个Set,反过来对这个Set的修改也会反映到Map 。特别地,当在这个Set上进行迭代的过程中,如果修改了Map(除非是通过这个Set的迭代器进行remov()e或setValue()操作),那么迭代过程产生的结果是不确定的。 

 

再看HashMap中entrySet()方法的实现

Java代码   收藏代码
  1. public Set> entrySet() {  
  2. urn entrySet0();  
  3. }  
  4.   
  5. private Set> entrySet0() {  
  6.     Set> es = entrySet;  
  7.     return es != null ? es : (entrySet = new EntrySet());  
  8. }  
  9.   
  10. private final class EntrySet extends AbstractSet> {  
  11.     public Iterator> iterator() {  
  12.         return newEntryIterator();  
  13.     }  
  14.     public boolean contains(Object o) {  
  15.         if (!(o instanceof Map.Entry))  
  16.             return false;  
  17.         Map.Entry e = (Map.Entry) o;  
  18.         Entry candidate = getEntry(e.getKey());  
  19.         return candidate != null && candidate.equals(e);  
  20.     }  
  21.     public boolean remove(Object o) {  
  22.         return removeMapping(o) != null;  
  23.     }  
  24.     public int size() {  
  25.         return size;  
  26.     }  
  27.     public void clear() {  
  28.         HashMap.this.clear();  
  29.     }  
  30. }  

 EntrySet是HashMap的一个内部类,它继承自AbstractSet,必须需要实现iterator()和size()两个抽象方法。而HashMap的entrySet()不过是返回了EntrySet的一个实例。

注意,与我们之前定义的SlowMap的entrySet()相比,这里的entrySet()实现中没有进行任何创建副本的操作。不难发现,remove()和clear()等方法,最终会调用到EntrySet所在的外部对象(即一个HashMap实例)的相关方法,从而实现对Map的修改应该会反映到这个Set,反过来对这个Set的修改也会反映到Map。当然,除了直接在entrySet()方法返回的Set对象上直接进行操作,还可以获取这个Set的迭代器,通过迭代器来修改Map和对应的Set。这类修改,同样也应当需要实现对Map的修改应该会反映到这个Set,反过来对这个Set的修改也会反映到Map。这里涉及到HashMap.EntryIterator类,内容较多,后面接着分析



你可能感兴趣的:(java,entrySet,map,java)