ArrayMap数据结构分析

Posted xingfeng_coder

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ArrayMap数据结构分析相关的知识,希望对你有一定的参考价值。

ArrayMap是android上特有的一个性能比较高的Map,和HashMap一样,也实现了Map接口。

这里只分析其数据结构部分,不分析其高效缓存部分。

分析

ArrayMap的结构是int[] mHashes,记录每个key的hash值;Object[] mArray记录Key和Value,对于每一组Key和Value,按照Key和Value的顺序排列。

put(K,V)时,首先根据K计算出来一个Hash值,然后在mHashes中使用二分查找来查找这个Hash值,既然能使用二分查找也就是说,这个mHashs数组是有序的,得到了index后,再在mArray中查找,其中index2表示key的位置,index2+1表示value的位置。

mHashes虽然是个有序的,但却是可以重复的;对于重复的mHashs,在mArray中进行匹配时是通过Key#equals()方法来判断是否相同的。

举个例子:

class SameHashObj(val num:Int)
    
    override fun equals(other: Any?): Boolean 
        if (this === other) return true
        if (other !is SameHashObj) return false

        if (num != other.num) return false

        return true
    

    override fun hashCode(): Int 
        return num%4
    

SameHashObj持有一个int,equals就是比较num值是否相同;而hashCode()这里为了测试,对4取了个模。

进行下列代码:

val arrayMap=ArrayMap<SameHashObj,String>()

        arrayMap[SameHashObj(0)] = "0"

        arrayMap[SameHashObj(1)] = "1"
        arrayMap[SameHashObj(2)] = "2"
        arrayMap[SameHashObj(3)] = "3"

        arrayMap[SameHashObj(4)] = "4"

最终结构是这样的:

对于SameHashObj(0)和SameHashObj(4),虽然hashCode都是0,但是其equals却不同,所以会出现上面的现象。

多说一点

ArrayMap一共有三个构造方法,其中有一个是hide的,如下:

public ArrayMap() 
        this(0, false);
    

    /**
     * Create a new ArrayMap with a given initial capacity.
     */
    public ArrayMap(int capacity) 
        this(capacity, false);
    

    /** @hide */
    public ArrayMap(int capacity, boolean identityHashCode) 
        mIdentityHashCode = identityHashCode;

        // If this is immutable, use the sentinal EMPTY_IMMUTABLE_INTS
        // instance instead of the usual EmptyArray.INT. The reference
        // is checked later to see if the array is allowed to grow.
        if (capacity < 0) 
            mHashes = EMPTY_IMMUTABLE_INTS;
            mArray = EmptyArray.OBJECT;
         else if (capacity == 0) 
            mHashes = EmptyArray.INT;
            mArray = EmptyArray.OBJECT;
         else 
            allocArrays(capacity);
        
        mSize = 0;
    

区别就是identityHashCode这个值的定义问题,我们一般能用到的就是false。下面试着用反射来初始化一个为true的ArrayMap,看看有啥变化。

结构如下图了:

可以看到mHashes数组,那是个啥鬼?

这个identityHashCode到底是啥作用呢?看名字,应该可以猜得出来,对于默认的情况,是允许mHashes中出现相同的值的,比如说上面的情况。

put()方法中有如下代码:

    public V put(K key, V value) 
        final int osize = mSize;
        final int hash;
        int index;
        if (key == null) 
            hash = 0;
            index = indexOfNull();
         else 
            hash = mIdentityHashCode ? System.identityHashCode(key) : key.hashCode();
            index = indexOf(key, hash);
        
        ...
        

如果mIdentityHashCode=true,那么会使用System.identityHashCode(key)来计算Key的hashCode,否则就是key的hashCode方法。

对于默认的情况,就会调用SameHashObj重写的hashCode()方法;对于true的情况下,最终会调用到没有重写hashCode()的情况,而默认的hashCode()方法返回的是每个对象的内存地址,所以自然每个不同的对象都是不同的,不管你hashCode()怎么实现,压根不去调你,就不会重复了。

参考

关注我的技术公众号,与君共同学习。微信扫一扫下方二维码即可关注:

以上是关于ArrayMap数据结构分析的主要内容,如果未能解决你的问题,请参考以下文章

ArrayMap数据结构分析

ArrayMap代码分析

HashMap,ArrayMap,SparseArray源码分析及性能对比

不再害怕面试问ArrayMap一文完全看懂Android ArrayMap源码解析

Android ArrayMap源码详解

数据结构HashMap(Android SparseArray 和ArrayMap)