关于:Map分析
AbstractMap是一个虚类,是HashMap、HashTable、ConcurrentHashMap等常用的重要实体类的“顶级”父类,具备常用实体类的底层实现细节。AbstractMap继承了Map,实现了Map的部分方法,Map分析中介绍过的方法将不再罗列,但会呈现关键逻辑。下面直接放关键的或者具有代表性的源码逻辑。
循环迭代判断或取值类型:
public boolean containsValue(Object value) {//是否包含value值,
Iterator<Entry<K,V>> i = entrySet().iterator();//通过Iterator迭代器实现value的遍历
if (value==null) {//null值不能调用equals方法,否则抛出空指针异常,因此将空value的情况单独讨论
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;
}
类似的还有containsKey(Object key)、get(Object key)等很多方法,逻辑都与containsValue类似,都是通过Iteration迭代器遍历Entry数组,如果符合条件,则进行相应操作并返回操作结果;AbstractMap.put(k, v)方法总是抛出不支持此类操作异常,需要其子类重载此方法。
获取key或value的集合:
transient volatile Set<K> keySet;//保存key的set集合
transient volatile Collection<V> values;//保存value的collection集合
//此处被transient关键字修饰的变量将不会在对象序列化的时候参与序列化
public Set<K> keySet() {//获得key的set集合,
if (keySet == null) {//如果keySet为空,则新建一个keySet通过返回一个匿名内部类实现
keySet = new AbstractSet<K>() {//借助AbstractSet虚类,实现一个匿名内部类;
public Iterator<K> iterator() {//获取一个保存key关键字的迭代器
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);
}
};
}
return keySet;
}
同理类似的有public Collection<V> values( )方法,获取一个值value的collection集合,原理与获取key的set集合类似,通过匿名内部类实现。
AbstractMap重写了equals和hashCode两个方法,用于判断对象是否相等,在map中允许重复的value,不允许重复的key,同时某些实体类的value和key值可以为null;AbstractMap方法的equals方法是对比两个集合里键值对的映射关系是否都是相等的,如果相等,则认为是相等的集合(传统的equals方法与物理层的地址有一定的关系)
public boolean equals(Object o) {//判断对象是否相等(为了简化叙述,'this'为我、'o'为对方)
if (o == this)//如果对方是我,那么我=我,返回true
return true;
if (!(o instanceof Map))//如果对方不是我或者我的子类,则直接返回false
return false;
Map<?,?> m = (Map<?,?>) o;//将对方向上转型,提升为和我一样的Map<?,?>型
if (m.size() != size())//如果我和对方的长度不一样,返回false
return false;
try {
Iterator<Entry<K,V>> i = entrySet().iterator();//获取我的迭代器
while (i.hasNext()) {
Entry<K,V> e = i.next();//我的一组entry映射关系
K key = e.getKey();//我的key
V value = e.getValue();//我的value
if (value == null) {//如果我的value值为null
if (!(m.get(key)==null && m.containsKey(key)))
//如果对方根据我的key获取的值也为空并且对方的keySet集合包含我的key,说明这一组映射关系是相等的,继续进行下一次比较
return false;
} else {//如果我的value值不为空,
if (!value.equals(m.get(key)))//调用我的value对象的equals方法判断对方根据我的key获取的value比较,如果相等,则继续下一次比较
return false;
}
}
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
//遍历完后,没有不相等的情况并且没有抛出异常,则我和对方是一样的
return true;
}
public int hashCode() {//获取hash值的方法,此处为获取集合里所有entry键值对哈希值的和
int h = 0;
Iterator<Entry<K,V>> i = entrySet().iterator();
while (i.hasNext())
h += i.next().hashCode();
return h;
}
在AbstractMap.equals方法中,并没有调用hashCode方法,并不是说可以不用重载hashCode方法;重载equals方法时,无论是否用到hashCode都应当重载hashCode方法,应该形成一种必须要遵守的约定,相当于在写代码时,一行最多只能有一个语句,且嵌套需要用缩进体现结构一样的约定。
public String toString( )方法就不用多说了,就是将集合的元素按照一定的格式输出,值得思考的是在此方法中,用的是StringBuilder来处理字符串,而不是用的String或者StringBuffer,String字符串的值是静态不可变的,当使用String处理集合中的元素时,如果集合元素过多,在拼接字符串时,会产生很多的字符串碎片,消耗的VM内存是极大的,应该避免使用String字符串处理大量的字符串;而StringBuilder和StringBuffer基本都是相同的,唯一的区别是StringBuffer是线程安全的,在对StringBuffer变量操作的时候存在获取锁,加锁,释放锁的过程,增加了系统的开销,性能比StringBuilder差一些;就算在多线程的条件下操作Map集合,也应该从Map层面实现线程安全(HashTable、ConcurrentHashMap、Collections.synchronizeMap),而不应该在某个单独的方法内再次实现。
AbstractMap的clone( )方法是protected的,并且是直接调用的Object.clone( )方法获取一个AbstractMap,并将其中的keySet和values制空;
AbstractMap有两个内部类SimpleEntry<K, V>和SimpleImmutableEntry<K, V>, SimpleEntry<K, V>类的头部声明是public static class SimpleEntry<K, V> implements Entry<K, V>, Serializable{},SimpleImmutableEntry<K, V>类的头部声明是public static class SimpleImmutableEntry<K,V> implements Entry<K,V>,Serializable,他们都是AbstractMap的静态内部类,并且都实现了Map.Entry<K, V>和Serializable接口;实现Entry接口,用于保存K-V映射关系,两者都重载了equals、hashCode和toString方法,如两者的名字一样,实现都比较简单,此处就不再赘述;两者的区别主要是final类型的变量不一样,SimpleEntry中key是用final修饰,初始化后是不可更改的,而SimpleImmutableEntry的key和value都是用final修饰的,初始化以后是不可更改的,两者都为其子类的具体实现提供一个简单的入口。另一方面,实现Serializable接口是为了实现对象序列化,所谓对象序列化,就是将对象的内容变换成一种定制的格式,方便数据的交互,为各个层面进行解耦;同时实现Serializable接口可以使用其子类默认的序列化与反序列化策略,也可以自己重载它的序列化与反序列化策略,定制序列化的格式;若对象中存在不想序列化的内容,如用户的password字段往往是不想要被序列化展示给其他层面的,此时可以用transient关键字修饰变量或者字段达到不参与序列化的目的,同时有其他的注意事项(transient关键字存在失效的情景),此处不再赘述。