AbstractMap源码分析

关于: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关键字存在失效的情景),此处不再赘述。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值