转载链接:http://410063005.iteye.com/blog/1676845
通过继承AbstractMap我们可以很容易实现自己的Map,我们只需要实现唯一的抽象的entrySet()方法。 以下是来自《Jav编程思想》(第四版第17章的例子),继承AbstractMap实现了自己的SlowMap。 另外还应该注意, 如果要创建自己的Map,还必须同时定义Map.Entry的实现。
总结起来实现自定义Map需要以下两个步骤:
- 继承AbstractMap需要实现entrySet()方法
- 实现自己的Map.Entry
- //: containers/MapEntry.java
- // A simple Map.Entry for sample Map implementations.
- import java.util.*;
- public class MapEntry<K,V> implements Map.Entry<K,V> {
- private K key;
- private V value;
- public MapEntry(K key, V value) {
- this.key = key;
- this.value = value;
- }
- public K getKey() { return key; }
- public V getValue() { return value; }
- public V setValue(V v) {
- V result = value;
- value = v;
- return result;
- }
- public int hashCode() {
- return (key==null ? 0 : key.hashCode()) ^
- (value==null ? 0 : value.hashCode());
- }
- public boolean equals(Object o) {
- if(!(o instanceof MapEntry)) return false;
- MapEntry me = (MapEntry)o;
- return
- (key == null ?
- me.getKey() == null : key.equals(me.getKey())) &&
- (value == null ?
- me.getValue()== null : value.equals(me.getValue()));
- }
- public String toString() { return key + "=" + value; }
- } ///:~
- //: containers/SlowMap.java
- // A Map implemented with ArrayLists.
- import java.util.*;
- import net.mindview.util.*;
- public class SlowMap<K,V> extends AbstractMap<K,V> {
- private List<K> keys = new ArrayList<K>();
- private List<V> values = new ArrayList<V>();
- public V put(K key, V value) {
- V oldValue = get(key); // The old value or null
- if(!keys.contains(key)) {
- keys.add(key);
- values.add(value);
- } else
- values.set(keys.indexOf(key), value);
- return oldValue;
- }
- public V get(Object key) { // key is type Object, not K
- if(!keys.contains(key))
- return null;
- return values.get(keys.indexOf(key));
- }
- public Set<Map.Entry<K,V>> entrySet() {
- Set<Map.Entry<K,V>> set= new HashSet<Map.Entry<K,V>>();
- Iterator<K> ki = keys.iterator();
- Iterator<V> vi = values.iterator();
- while(ki.hasNext())
- set.add(new MapEntry<K,V>(ki.next(), vi.next()));
- return set;
- }
- public static void main(String[] args) {
- SlowMap<String,String> m= new SlowMap<String,String>();
- m.putAll(Countries.capitals(15));
- System.out.println(m);
- System.out.println(m.get("BULGARIA"));
- System.out.println(m.entrySet());
- }
- } /* Output:
- {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}
- Sofia
- [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]
- *///:~
以上是一个参考实现。这个解决方案看起来很简单并且没什么问题,但这并不是一个恰当的实现。主要问题是它创建了键和值的副本。entrySet()的恰当实现 应该在Map中提供视图,而不是副本,并且这个视图允许对原始的映射表进行修改,而副本则不行。
可以参考源码来学习正确的entrySet()应该如何实现。首先先看Map中entrySet()的声明
- public interface Map<K,V> {
- /**
- * Returns a {@link Set} view of the mappings contained in this map.
- * The set is backed by the map, so changes to the map are
- * reflected in the set, and vice-versa. If the map is modified
- * while an iteration over the set is in progress (except through
- * the iterator's own <tt>remove</tt> operation, or through the
- * <tt>setValue</tt> operation on a map entry returned by the
- * iterator) the results of the iteration are undefined. The set
- * supports element removal, which removes the corresponding
- * mapping from the map, via the <tt>Iterator.remove</tt>,
- * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and
- * <tt>clear</tt> operations. It does not support the
- * <tt>add</tt> or <tt>addAll</tt> operations.
- *
- * @return a set view of the mappings contained in this map
- */
- Set<Map.Entry<K, V>> entrySet();
- }
再看HashMap中entrySet()方法的实现
- public Set<Map.Entry<K,V>> entrySet() {
- urn entrySet0();
- }
- private Set<Map.Entry<K,V>> entrySet0() {
- Set<Map.Entry<K,V>> es = entrySet;
- return es != null ? es : (entrySet = new EntrySet());
- }
- private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
- public Iterator<Map.Entry<K,V>> iterator() {
- return newEntryIterator();
- }
- public boolean contains(Object o) {
- if (!(o instanceof Map.Entry))
- return false;
- Map.Entry<K,V> e = (Map.Entry<K,V>) o;
- Entry<K,V> candidate = getEntry(e.getKey());
- return candidate != null && candidate.equals(e);
- }
- public boolean remove(Object o) {
- return removeMapping(o) != null;
- }
- public int size() {
- return size;
- }
- public void clear() {
- HashMap.this.clear();
- }
- }
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类,内容较多,后面接着分析