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

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

源码分析

构造方法

 protected AbstractMap() {
}

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

成员变量

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

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

抽象方法

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

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

实例方法

查询方法

     public int size() {
return entrySet().size();
}

此方法返回集合长度

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

判断Map是否为空

     public boolean containsValue(Object value) {
Iterator<Entry<K,V>> i = entrySet().iterator();
if (value==null) {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (e.getValue()==null)
return true;
}
} else {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (value.equals(e.getValue()))
return true;
}
}
return false;
}

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

     public boolean containsKey(Object key) {
Iterator<Map.Entry<K,V>> i = entrySet().iterator();
if (key==null) {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (e.getKey()==null)
return true;
}
} else {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (key.equals(e.getKey()))
return true;
}
}
return false;
}

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

     public V get(Object key) {
Iterator<Entry<K,V>> i = entrySet().iterator();
if (key==null) {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (e.getKey()==null)
return e.getValue();
}
} else {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (key.equals(e.getKey()))
return e.getValue();
}
}
return null;
}

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

修改操作

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

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

     public V remove(Object key) {
Iterator<Entry<K,V>> i = entrySet().iterator();
Entry<K,V> correctEntry = null;
if (key==null) {
while (correctEntry==null && i.hasNext()) {
Entry<K,V> e = i.next();
if (e.getKey()==null)
correctEntry = e;
}
} else {
while (correctEntry==null && i.hasNext()) {
Entry<K,V> e = i.next();
if (key.equals(e.getKey()))
correctEntry = e;
}
} V oldValue = null;
if (correctEntry !=null) {
oldValue = correctEntry.getValue();
i.remove();
}
return oldValue;
}

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

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

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

     public void clear() {
entrySet().clear();
}

清空Map

视图

返回所有key的set集合:

     public Set<K> keySet() {
Set<K> ks = keySet;
if (ks == null) {
ks = new AbstractSet<K>() {
public Iterator<K> iterator() {
return new Iterator<K>() {
private Iterator<Entry<K,V>> i = entrySet().iterator(); public boolean hasNext() {
return i.hasNext();
} public K next() {
return i.next().getKey();
} 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 k) {
return AbstractMap.this.containsKey(k);
}
};
keySet = ks;
}
return ks;
}

这里可以看到是使用了匿名内部类的方法 ,通过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是否相等,代码很好懂,就不做过多的解释了。

     public boolean equals(Object o) {
if (o == this)
return true; if (!(o instanceof Map))
return false;
Map<?,?> m = (Map<?,?>) o;
if (m.size() != size())
return false; try {
Iterator<Entry<K,V>> i = entrySet().iterator();
while (i.hasNext()) {
Entry<K,V> e = i.next();
K key = e.getKey();
V value = e.getValue();
if (value == null) {
if (!(m.get(key)==null && m.containsKey(key)))
return false;
} else {
if (!value.equals(m.get(key)))
return false;
}
}
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
} return true;
}

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

     public int hashCode() {
int h = 0;
Iterator<Entry<K,V>> i = entrySet().iterator();
while (i.hasNext())
h += i.next().hashCode();
return h;
}

其他方法

覆盖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()实现浅拷贝

     protected Object clone() throws CloneNotSupportedException {
AbstractMap<?,?> result = (AbstractMap<?,?>)super.clone();
result.keySet = null;
result.values = null;
return result;
}

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

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

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

SimpleEntry

     public static class SimpleEntry<K,V>
implements Entry<K,V>, java.io.Serializable
{
private static final long serialVersionUID = -8499721149061103585L; private final K key;
private V value; /**
* Creates an entry representing a mapping from the specified
* key to the specified value.
*
* @param key the key represented by this entry
* @param value the value represented by this entry
*/
public SimpleEntry(K key, V value) {
this.key = key;
this.value = value;
} /**
* Creates an entry representing the same mapping as the
* specified entry.
*
* @param entry the entry to copy
*/
public SimpleEntry(Entry<? extends K, ? extends V> entry) {
this.key = entry.getKey();
this.value = entry.getValue();
} public K getKey() {
return key;
} public V getValue() {
return value;
} public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue;
} public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
return eq(key, e.getKey()) && eq(value, e.getValue());
} public int hashCode() {
return (key == null ? 0 : key.hashCode()) ^
(value == null ? 0 : value.hashCode());
} public String toString() {
return key + "=" + value;
} }

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

SimpleImmutableEntry

     public static class SimpleImmutableEntry<K,V>
implements Entry<K,V>, java.io.Serializable
{
private static final long serialVersionUID = 7138329143949025153L; private final K key;
private final V value; public SimpleImmutableEntry(K key, V value) {
this.key = key;
this.value = value;
} public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry) {
this.key = entry.getKey();
this.value = entry.getValue();
} public K getKey() {
return key;
} public V getValue() {
return value;
} public V setValue(V value) {
throw new UnsupportedOperationException();
} public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
return eq(key, e.getKey()) && eq(value, e.getValue());
} public int hashCode() {
return (key == null ? 0 : key.hashCode()) ^
(value == null ? 0 : value.hashCode());
} public String toString() {
return key + "=" + value;
} }

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

实现一个可以赋值的Map

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

 public class CanPutMap<K,V> extends AbstractMap<K,V> implements Map<K,V> {

     private Node<K,V> table[];

     private int index;

     private Set<Entry<K,V>> entrySet;

     public CanPutMap() {
table = (Node<K,V>[])new Node[];
index = ;
} public V put(K key, V value) {
V oldValue = null;
for(int i = ; i < table.length; i++) {
if(table[i] != null && table[i].getKey() == key) {
oldValue = table[i].getValue();
table[i].setValue(value);
break;
}
}
if(oldValue == null) {
table[index++] = new Node<K,V>(key, value);
}
return oldValue;
} public V get(Object key) {
V value = null;
for(int i = ; i < table.length; i++) {
if(table[i] != null && table[i].getKey() == key) {
value = table[i].getValue();
break;
}
}
return value;
} @Override
public Set<Entry<K,V>> entrySet() {
// TODO Auto-generated method stub
Set<Entry<K,V>> es;
return (es = entrySet) == null ? entrySet = new MySet(): es;
} final class MySet extends AbstractSet<Entry<K,V>> { @Override
public Iterator<java.util.Map.Entry<K, V>> iterator() {
// TODO Auto-generated method stub
return new MyIterator();
} @Override
public int size() {
// TODO Auto-generated method stub
return ;
} } final class MyIterator implements Iterator<Entry<K,V>> { Node<K,V> current; Node<K,V> next; int index; public MyIterator() {
Node<K,V>[] t = table;
current = next = null;
index = ;
if(t != null && table.length > ) {
do{}while(index < table.length && (next = table[index++]) == null);
}
} @Override
public boolean hasNext() {
// TODO Auto-generated method stub
return next != null;
} @Override
public java.util.Map.Entry<K, V> next() {
// TODO Auto-generated method stub
Node<K,V> e = next;
current = next;
do{}while(index < table.length && (next = table[index++]) == null);
return e;
} public final void remove() {
for(int i = ; i < table.length; i++) {
if(table[i] == current) {
table[i] = null;
}
}
} } public static class Node<K,V> implements Entry<K,V> { private final K key;
private V value; public Node(K key, V value) {
this.key = key;
this.value = value;
} @Override
public K getKey() {
// TODO Auto-generated method stub
return key;
} @Override
public V getValue() {
// TODO Auto-generated method stub
return value;
} @Override
public V setValue(V value) {
// TODO Auto-generated method stub
V oldValue = this.value;
this.value = value;
return oldValue;
} } public static void main(String[] args) {
Map<String, Integer> map = new CanPutMap<String, Integer>();
map.put("", );
map.put("", );
map.put("", );
for(Entry<String, Integer> e : map.entrySet()) {
System.out.println(e.getKey() + "->" + e.getValue());
}
for(Iterator<Entry<String, Integer>> i = map.entrySet().iterator(); i.hasNext();) {
Entry<String, Integer> e = i.next();
System.out.println(e.getKey() + "->" + e.getValue());
if(e.getKey().equals("")) {
i.remove();
}
}
for(Entry<String, Integer> e : map.entrySet()) {
System.out.println(e.getKey() + "->" + e.getValue());
}
} }

随笔2 AbstractMap<K,V>的更多相关文章

  1. 随笔3 HashMap<K,V>

    equals.hashcode和==的区别 在介绍HashMap之前,我想先阐述一下我对这三者的理解,equals这个方法呢,就是在判断是否为同一对象(注意,这里的同一对象和相同的内存地址是不同的), ...

  2. 随笔4 Dictionary<K,V>

    本来说是想介绍一下Hashtable的,但是发现HashMap和Hashtable最开始的不同就是在于HashMap继承了AbstractMap,而Hashtable继承了Dictionary< ...

  3. Java源码 HashMap<K,V>

    HashMap类 https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html public class HashMap<K, ...

  4. 随笔1 interface Map<K,V>

    第一次写笔记就从map开始吧,如上图所示,绿色的是interface,黄色的是abstract class,蓝色的是class,可以看出所有和图相关的接口,抽象类和类的起源都是interface ma ...

  5. java:警告:[unchecked] 对作为普通类型 java.util.HashMap 的成员的put(K,V) 的调用未经检查

    java:警告:[unchecked] 对作为普通类型 java.util.HashMap 的成员的put(K,V) 的调用未经检查 一.问题:学习HashMap时候,我做了这样一个程序: impor ...

  6. Java集合源码分析(七)HashMap<K, V>

    一.HashMap概述 HashMap基于哈希表的 Map 接口的实现.此实现提供所有可选的映射操作,并允许使用 null 值和 null 键.(除了不同步和允许使用 null 之外,HashMap  ...

  7. JAVA泛型? T K V E等代表的意思

    ? 表示不确定的java类型. T  表示java类型. K V 分别代表java键值中的Key Value. E 代表Element. Object跟这些东西代表的java类型有啥区别呢? Obje ...

  8. Array,ArrayList、List<T>、HashSet<T>、LinkedList<T>与Dictionary<K,V>

    Array: 数组在C#中最早出现的.在内存中是连续存储的,所以它的索引速度非常快,而且赋值与修改元素也很简单. 但是数组存在一些不足的地方.在数组的两个数据间插入数据是很麻烦的,而且在声明数组的时候 ...

  9. ArrayList,Hashtable,List<T>,Dictionary<K,V>

    1.ArrayList ArrayList list = new ArrayList(); //for遍历 ; i < list.Count; i++) { SE se=(SE)list[i]; ...

随机推荐

  1. 《SQL Server 2012 T-SQL基础》读书笔记 - 5.表表达式

    Chapter 5 Table Expressions 一个表表达式(table expression)是一个命名的查询表达式,代表一个有效的关系表.SQL Server包括4种表表达式:派生表(de ...

  2. Linux内核调试方法总结之ltrace

    ltrace [用途] 库文件调用跟踪器,Linux内核内建命令,用法类似strace [命令格式] [参数说明][详细说明参考man ltrace帮助文档] -D 打印调试信息 01-DEBUG_G ...

  3. PROD异机全备份恢复验证实施文档

    PROD异机全备份恢复验证实施文档 ******利用10月25日的全量备份.10月26日当天的归档日志及当前redo日志,恢复数据库到故障前数据库状态****** 准备工作:source 源库:PRO ...

  4. Basic Model Theory of XPath on Data Trees

    w https://openproceedings.org/2014/conf/icdt/FigueiraFA14.pdf From a database perspective, however, ...

  5. vue 的sync用法

    这个关键字在v2.3.0+ 新增,注意带有 .sync 修饰符的 v-bind 不能和表达式一起使用 (例如 v-bind:title.sync=”doc.title + ‘!’” 是无效的).说白了 ...

  6. sqlite时间类型

    SQLite分页显示:Select * From news order by id desc Limit 10 Offset 10这篇文章是根据 SQLite 官方 WIKI 里的内容翻译,如果有什么 ...

  7. 熟练使用jquery ui / easyui的各种组件设计网页界面

    熟练使用jquery ui / easyui的各种组件设计网页界面

  8. error: exportArchive: You don’t have permission to save the file “HelloWorld.ipa” in the folder “HelloWorld”.

    成功clean环境和生成archive文件之后,最后一步导出ipa包,遇到了权限问题: you don’t have permission to save the file “HelloWorld.i ...

  9. Linux_VMWare12 Install RHEL7

    目录 目录 前言 Install RHEL7 前言 准备考试,顺便来一波VMWare安装虚拟机的图文详解. Install RHEL7 step1. 选择自定义安装,Next step2. 版本兼容性 ...

  10. Delphi XE2 之 FireMonkey 入门(29) - 数据绑定: TBindingsList: 表达式的 Evaluate() 方法

    Delphi XE2 之 FireMonkey 入门(29) - 数据绑定: TBindingsList: 表达式的 Evaluate() 方法 TBindingsList 中可能不止一个表达式, 通 ...