随笔2 AbstractMap<K,V>

上一篇写了Map接口的源码分析,这一篇写一下Map接口的一个实现类AbstractMap,从名字就可以看出这是一个抽象类,提供了Map接口的骨架实现,为我们实现Map接口的时候提供了很大的便利。在这里类中,还有一个抽象方法entrySet没有被实现,在实现的方法中put方法也仅仅抛出了一个异常。我们在继承这个类写自己的Map时,如果是一个不支持赋值的Map,那么只需要实现entrySet方法。如果是实现一个可以添加键值对的Map,那么不仅要实现entrySet方法,还需要在entrySet返回的那个迭代器中的remove方法。

本篇会参考http://blog.csdn.net/Airsaid/article/details/51178444写的分析的组织方式,并最后简单的实现一个Map作为实例

源码分析

构造方法

1 protected AbstractMap() {
2 }

在Map的建议中,是希望有两个构造函数,一个是无参构造函数,另一个是以Map为参数的构造函数,但此处只实现了一个无参构造函数,给子类提供了一个构造方法

成员变量

 

1 transient Set<K>        keySet;
2 transient Collection<V> values;

 

这两个成员将分别保存Map当中的键和值,因为Map要保证键的唯一性,所以在保存键的时候使用了Set<K>这种数据结构,而Map中的值是可以重复的,所以要使用Collection<V>这种数据结构来存储。在这里还要说一下transient关键字,由于AbstractMap的子类HashMap等都实现了Serializable接口,但是在序列化的时候,AbstractMap不希望暴露底层的数据集,所以添加transient,让这两个成员变量不被序列化。我看到有些源码分析说这两个变量被volatile关键字修饰,目的是保证一定的线程安全,但是我在jdk1.8中并没有看到。

抽象方法

 

1 public abstract Set<Entry<K,V>> entrySet();

 

个人感觉这个地方写的很精髓,这个类中的大部分实现的方法会调用这个方法,而这个方法将底层数据结构的实现方式交给了他的派生类,确实把AbstractMap抽象到了一个很高的高度,极大的降低了和底层的耦合度。而且由于Set的实现方式不同,导致导出的迭代器也不一样,感觉这里特别值得学习。

实例方法

查询方法

1     public int size() {
2         return entrySet().size();
3     }

此方法返回集合长度

 

1     public boolean isEmpty() {
2         return size() == 0;
3     }

判断Map是否为空

 

 1     public boolean containsValue(Object value) {
 2         Iterator<Entry<K,V>> i = entrySet().iterator();
 3         if (value==null) {
 4             while (i.hasNext()) {
 5                 Entry<K,V> e = i.next();
 6                 if (e.getValue()==null)
 7                     return true;
 8             }
 9         } else {
10             while (i.hasNext()) {
11                 Entry<K,V> e = i.next();
12                 if (value.equals(e.getValue()))
13                     return true;
14             }
15         }
16         return false;
17     }

查询Map中是否存在目标值,这里允许保存null

 

 1     public boolean containsKey(Object key) {
 2         Iterator<Map.Entry<K,V>> i = entrySet().iterator();
 3         if (key==null) {
 4             while (i.hasNext()) {
 5                 Entry<K,V> e = i.next();
 6                 if (e.getKey()==null)
 7                     return true;
 8             }
 9         } else {
10             while (i.hasNext()) {
11                 Entry<K,V> e = i.next();
12                 if (key.equals(e.getKey()))
13                     return true;
14             }
15         }
16         return false;
17     }

这里同上,只不过是查询key的值

 

 1     public V get(Object key) {
 2         Iterator<Entry<K,V>> i = entrySet().iterator();
 3         if (key==null) {
 4             while (i.hasNext()) {
 5                 Entry<K,V> e = i.next();
 6                 if (e.getKey()==null)
 7                     return e.getValue();
 8             }
 9         } else {
10             while (i.hasNext()) {
11                 Entry<K,V> e = i.next();
12                 if (key.equals(e.getKey()))
13                     return e.getValue();
14             }
15         }
16         return null;
17     }

通过key查询value,当value存在时返回value,否则返回null

 

修改操作

 

1     public V put(K key, V value) {
2         throw new UnsupportedOperationException();
3     }

 

添加新的键值对,如果子类需要修改操作的时候需要重写这个方法,这里我感觉也是这个类的一个亮点,这里并没有留出一个空方法,而是抛出一个异常,是的在别人忘记实现普通方法时可以得到明确的提示

 

 1     public V remove(Object key) {
 2         Iterator<Entry<K,V>> i = entrySet().iterator();
 3         Entry<K,V> correctEntry = null;
 4         if (key==null) {
 5             while (correctEntry==null && i.hasNext()) {
 6                 Entry<K,V> e = i.next();
 7                 if (e.getKey()==null)
 8                     correctEntry = e;
 9             }
10         } else {
11             while (correctEntry==null && i.hasNext()) {
12                 Entry<K,V> e = i.next();
13                 if (key.equals(e.getKey()))
14                     correctEntry = e;
15             }
16         }
17 
18         V oldValue = null;
19         if (correctEntry !=null) {
20             oldValue = correctEntry.getValue();
21             i.remove();
22         }
23         return oldValue;
24     }

当指定的key存在时,会删除对应的键值对,返回对应的值,如果不存在,则返回null。这里要注意的是需要子类重写迭代器的remove方法

 

 

 

 

 

1     public void putAll(Map<? extends K, ? extends V> m) {
2         for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
3             put(e.getKey(), e.getValue());
4     }

 

将指定的Map中的元素放入本Map中,这里要注意如果没有实现put方法会报出异常

 

1     public void clear() {
2         entrySet().clear();
3     }

清空Map

 

视图

返回所有key的set集合:

 1     public Set<K> keySet() {
 2         Set<K> ks = keySet;
 3         if (ks == null) {
 4             ks = new AbstractSet<K>() {
 5                 public Iterator<K> iterator() {
 6                     return new Iterator<K>() {
 7                         private Iterator<Entry<K,V>> i = entrySet().iterator();
 8 
 9                         public boolean hasNext() {
10                             return i.hasNext();
11                         }
12 
13                         public K next() {
14                             return i.next().getKey();
15                         }
16 
17                         public void remove() {
18                             i.remove();
19                         }
20                     };
21                 }
22 
23                 public int size() {
24                     return AbstractMap.this.size();
25                 }
26 
27                 public boolean isEmpty() {
28                     return AbstractMap.this.isEmpty();
29                 }
30 
31                 public void clear() {
32                     AbstractMap.this.clear();
33                 }
34 
35                 public boolean contains(Object k) {
36                     return AbstractMap.this.containsKey(k);
37                 }
38             };
39             keySet = ks;
40         }
41         return ks;
42     }

这里可以看到是使用了匿名内部类的方法 ,通过AbstractSet来返回了一个Set,而AbstractSet这个抽象类中看似都实现了方法,但是在他的父类AbstractCollection和他实现的接口Set中,都有未实现的方法iterator()和size()两个抽象方法,所以这两个方法是必须实现的,而在iterator()这个方法中又使用匿名内部类的方式通过接口Iterator来生成一个迭代器,这个接口只有两个方法需要实现hasNext()和next()这两个方法,但是在Iterator中remove()方法只是抛出了一个不支持方法的异常,所以这里仍然需要重写。

 

返回所有value的collection集合

public Collection<V> values() {
        Collection<V> vals = values;
        if (vals == null) {
            vals = new AbstractCollection<V>() {
                public Iterator<V> iterator() {
                    return new Iterator<V>() {
                        private Iterator<Entry<K,V>> i = entrySet().iterator();

                        public boolean hasNext() {
                            return i.hasNext();
                        }

                        public V next() {
                            return i.next().getValue();
                        }

                        public void remove() {
                            i.remove();
                        }
                    };
                }

                public int size() {
                    return AbstractMap.this.size();
                }

                public boolean isEmpty() {
                    return AbstractMap.this.isEmpty();
                }

                public void clear() {
                    AbstractMap.this.clear();
                }

                public boolean contains(Object v) {
                    return AbstractMap.this.containsValue(v);
                }
            };
            values = vals;
        }
        return vals;
    }

这个方法和上一个很类似,唯一不同的是第一次使用匿名内部类的时候用的是AbstractCollection,但由于AbstractSet的父类就是AbstractCollection,所以本质并没有什么区别

 

比较和散列

比较指定的对象与当前Map是否相等,代码很好懂,就不做过多的解释了。

 1     public boolean equals(Object o) {
 2         if (o == this)
 3             return true;
 4 
 5         if (!(o instanceof Map))
 6             return false;
 7         Map<?,?> m = (Map<?,?>) o;
 8         if (m.size() != size())
 9             return false;
10 
11         try {
12             Iterator<Entry<K,V>> i = entrySet().iterator();
13             while (i.hasNext()) {
14                 Entry<K,V> e = i.next();
15                 K key = e.getKey();
16                 V value = e.getValue();
17                 if (value == null) {
18                     if (!(m.get(key)==null && m.containsKey(key)))
19                         return false;
20                 } else {
21                     if (!value.equals(m.get(key)))
22                         return false;
23                 }
24             }
25         } catch (ClassCastException unused) {
26             return false;
27         } catch (NullPointerException unused) {
28             return false;
29         }
30 
31         return true;
32     }

 

返回当前Map的hashcode,对每一个Entry对象都需要计算

1     public int hashCode() {
2         int h = 0;
3         Iterator<Entry<K,V>> i = entrySet().iterator();
4         while (i.hasNext())
5             h += i.next().hashCode();
6         return h;
7     }

 

其他方法

覆盖Object的toString()方法,这里注意编程细节,这里使用了StringBuilder,这种可以追加字符串的类,这里应该是为了效率而放弃了线程安全,没有使用StringBuffer

 

    public String toString() {
        Iterator<Entry<K,V>> i = entrySet().iterator();
        if (! i.hasNext())
            return "{}";

        StringBuilder sb = new StringBuilder();
        sb.append('{');
        for (;;) {
            Entry<K,V> e = i.next();
            K key = e.getKey();
            V value = e.getValue();
            sb.append(key   == this ? "(this Map)" : key);
            sb.append('=');
            sb.append(value == this ? "(this Map)" : value);
            if (! i.hasNext())
                return sb.append('}').toString();
            sb.append(',').append(' ');
        }
    }

 

 

覆盖Object的clone()实现浅拷贝

1     protected Object clone() throws CloneNotSupportedException {
2         AbstractMap<?,?> result = (AbstractMap<?,?>)super.clone();
3         result.keySet = null;
4         result.values = null;
5         return result;
6     }

 

私有静态方法,用来判断两个对象是否相等,这是给接下来两个静态内部类使用的

1     private static boolean eq(Object o1, Object o2) {
2         return o1 == null ? o2 == null : o1.equals(o2);
3     }

 

接下来这两个类都是Map.Entry<K,V>的实现类,一个是可以修改值的,一个是不可修改值的,从命名就可以看出,是给同学们自己实现Map接口的时候,打个样怎么样去实现一个Entry接口,由于这两个类都是静态公有内部类,其访问方式和普通的public类没什么区别,不需要依附于外部类AbstractMap的实例中,并且只能访问AbstractMap的静态区域

SimpleEntry

 

 1     public static class SimpleEntry<K,V>
 2         implements Entry<K,V>, java.io.Serializable
 3     {
 4         private static final long serialVersionUID = -8499721149061103585L;
 5 
 6         private final K key;
 7         private V value;
 8 
 9         /**
10          * Creates an entry representing a mapping from the specified
11          * key to the specified value.
12          *
13          * @param key the key represented by this entry
14          * @param value the value represented by this entry
15          */
16         public SimpleEntry(K key, V value) {
17             this.key   = key;
18             this.value = value;
19         }
20 
21         /**
22          * Creates an entry representing the same mapping as the
23          * specified entry.
24          *
25          * @param entry the entry to copy
26          */
27         public SimpleEntry(Entry<? extends K, ? extends V> entry) {
28             this.key   = entry.getKey();
29             this.value = entry.getValue();
30         }
31 
32         public K getKey() {
33             return key;
34         }
35 
36         public V getValue() {
37             return value;
38         }
39 
40 
41         public V setValue(V value) {
42             V oldValue = this.value;
43             this.value = value;
44             return oldValue;
45         }
46 
47 
48         public boolean equals(Object o) {
49             if (!(o instanceof Map.Entry))
50                 return false;
51             Map.Entry<?,?> e = (Map.Entry<?,?>)o;
52             return eq(key, e.getKey()) && eq(value, e.getValue());
53         }
54 
55         public int hashCode() {
56             return (key   == null ? 0 :   key.hashCode()) ^
57                    (value == null ? 0 : value.hashCode());
58         }
59 
60 
61         public String toString() {
62             return key + "=" + value;
63         }
64 
65     }

 

这里我么你来关注一下hashCode()方法,这里是用key和value做与操作,头一次看见,感觉很特别

 

SimpleImmutableEntry

 

 1     public static class SimpleImmutableEntry<K,V>
 2         implements Entry<K,V>, java.io.Serializable
 3     {
 4         private static final long serialVersionUID = 7138329143949025153L;
 5 
 6         private final K key;
 7         private final V value;
 8 
 9         public SimpleImmutableEntry(K key, V value) {
10             this.key   = key;
11             this.value = value;
12         }
13 
14         public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry) {
15             this.key   = entry.getKey();
16             this.value = entry.getValue();
17         }
18 
19         public K getKey() {
20             return key;
21         }
22 
23         public V getValue() {
24             return value;
25         }
26 
27         public V setValue(V value) {
28             throw new UnsupportedOperationException();
29         }
30 
31         public boolean equals(Object o) {
32             if (!(o instanceof Map.Entry))
33                 return false;
34             Map.Entry<?,?> e = (Map.Entry<?,?>)o;
35             return eq(key, e.getKey()) && eq(value, e.getValue());
36         }
37 
38         public int hashCode() {
39             return (key   == null ? 0 :   key.hashCode()) ^
40                    (value == null ? 0 : value.hashCode());
41         }
42 
43         public String toString() {
44             return key + "=" + value;
45         }
46 
47     }

 

不同之处在于setValue方法,直接抛出不支持异常

 

实现一个可以赋值的Map

 这个Map实现的很简单,赋值十次以后就错了呦,没有增加扩展功能,喜欢的同学可以自己加一个试试

  1 public class CanPutMap<K,V> extends AbstractMap<K,V> implements Map<K,V> {
  2     
  3     private Node<K,V> table[];
  4     
  5     private int index;
  6     
  7     private Set<Entry<K,V>> entrySet;
  8     
  9     public CanPutMap() {
 10         table = (Node<K,V>[])new Node[10];
 11         index = 0;
 12     }
 13     
 14     public V put(K key, V value) {
 15         V oldValue = null;
 16         for(int i = 0; i < table.length; i++) {
 17             if(table[i] != null && table[i].getKey() == key) {
 18                 oldValue = table[i].getValue();
 19                 table[i].setValue(value);
 20                 break;
 21             }
 22         }
 23         if(oldValue == null) {
 24             table[index++] = new Node<K,V>(key, value);
 25         }
 26         return oldValue;
 27     }
 28     
 29     public V get(Object key) {
 30         V value = null;
 31         for(int i = 0; i < table.length; i++) {
 32             if(table[i] != null && table[i].getKey() == key) {
 33                 value = table[i].getValue();
 34                 break;
 35             }
 36         }
 37         return value;
 38     }
 39 
 40     @Override
 41     public Set<Entry<K,V>> entrySet() {
 42         // TODO Auto-generated method stub
 43         Set<Entry<K,V>> es;
 44         return (es = entrySet) == null ? entrySet = new MySet(): es;
 45     }
 46     
 47     final class MySet extends AbstractSet<Entry<K,V>> {
 48 
 49         @Override
 50         public Iterator<java.util.Map.Entry<K, V>> iterator() {
 51             // TODO Auto-generated method stub
 52             return new MyIterator();
 53         }
 54 
 55         @Override
 56         public int size() {
 57             // TODO Auto-generated method stub
 58             return 0;
 59         }
 60         
 61     }
 62     
 63     final class MyIterator implements Iterator<Entry<K,V>> {
 64         
 65         Node<K,V> current;
 66         
 67         Node<K,V> next;
 68         
 69         int index;
 70         
 71         public MyIterator() {
 72             Node<K,V>[] t = table;
 73             current = next = null;
 74             index = 0;
 75             if(t != null && table.length > 0) {
 76                 do{}while(index < table.length && (next = table[index++]) == null);
 77             }
 78         }
 79         
 80         
 81 
 82         @Override
 83         public boolean hasNext() {
 84             // TODO Auto-generated method stub
 85             return next != null;
 86         }
 87 
 88         @Override
 89         public java.util.Map.Entry<K, V> next() {
 90             // TODO Auto-generated method stub
 91             Node<K,V> e = next;
 92             current = next;
 93             do{}while(index < table.length && (next = table[index++]) == null);
 94             return e;
 95         }
 96         
 97         public final void remove() {
 98             for(int i = 0; i < table.length; i++) {
 99                 if(table[i] == current) {
100                     table[i] = null;
101                 }
102             }
103         }
104         
105     }
106     
107     
108     public static class Node<K,V> implements Entry<K,V> {
109 
110         private final K key;
111         private V value;
112         
113         
114         public Node(K key, V value) {
115             this.key = key;
116             this.value = value;
117         }
118         
119         @Override
120         public K getKey() {
121             // TODO Auto-generated method stub
122             return key;
123         }
124 
125         @Override
126         public V getValue() {
127             // TODO Auto-generated method stub
128             return value;
129         }
130 
131         @Override
132         public V setValue(V value) {
133             // TODO Auto-generated method stub
134             V oldValue = this.value;
135             this.value = value;
136             return oldValue;
137         }
138         
139     }
140     
141     public static void main(String[] args) {
142         Map<String, Integer> map = new CanPutMap<String, Integer>();
143         map.put("123", 1);
144         map.put("456", 1);
145         map.put("123", 2);
146         for(Entry<String, Integer> e : map.entrySet()) {
147             System.out.println(e.getKey() + "->" + e.getValue());
148         }
149         for(Iterator<Entry<String, Integer>> i = map.entrySet().iterator(); i.hasNext();) {
150             Entry<String, Integer> e = i.next();
151             System.out.println(e.getKey() + "->" + e.getValue());
152             if(e.getKey().equals("456")) {
153                 i.remove();
154             }
155         }
156         for(Entry<String, Integer> e : map.entrySet()) {
157             System.out.println(e.getKey() + "->" + e.getValue());
158         }
159     }
160 
161 }

 

posted on 2018-03-21 23:36  徐彬彬要好好学习  阅读(364)  评论(0编辑  收藏  举报