随笔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 }