容器類框架分析(7)(Android)ArrayMap源碼分析

移步數(shù)據(jù)結(jié)構(gòu)--容器匯總(java & Android)

  • ArrayMap及SparseArray是android的系統(tǒng)API,是專門為移動設(shè)備而定制的。
  • ArrayMap是一個通用的key-value映射關(guān)系,它的映射存儲在數(shù)組數(shù)據(jù)結(jié)構(gòu)中。
  • 一個int數(shù)組用來存儲每一項的hashcode值,一個Object數(shù)組用來存儲key-value對。
    ArrayMap查找是通過二分查找來實現(xiàn)的,ArrayMap數(shù)組容量不會自動收縮的。
    593bb3e723a74.png

1 簡介

public final class ArrayMap<K, V> implements Map<K, V> {
        int[] mHashes;//保存key的hash值的int數(shù)組
        Object[] mArray;//保存key-value的Object數(shù)組
        int mSize;//元素的個數(shù)
    }
  • ArrayMap 實現(xiàn)了implements Map<K, V>接口,所以它也是一個關(guān)聯(lián)數(shù)組、哈希表。存儲以key->value 結(jié)構(gòu)形式的數(shù)據(jù)。它也是線程不安全的,允許key為null,value為null
  • 它相比HashMap,空間效率更高
  • 它的內(nèi)部實現(xiàn)是基于兩個數(shù)組
    • 一個int[]數(shù)組,用于保存每個item的hashCode.
    • 一個Object[]數(shù)組,保存key/value鍵值對。容量是上一個數(shù)組的兩倍。它可以避免在將數(shù)據(jù)插入Map中時額外的空間消耗(對比HashMap)。
  • 而且它擴容的更合適,擴容時只需要數(shù)組拷貝工作,不需要重建哈希表
  • 和HashMap相比,它不僅有擴容功能,在刪除時,如果集合剩余元素少于一定閾值,還有收縮(shrunk)功能。減少空間占用。
  • 對于哈希沖突的解決,查看源碼可以得知采用的是開放地址法。
  • 但是它不適合大容量的數(shù)據(jù)存儲。存儲大量數(shù)據(jù)時,它的性能將退化至少50%。
  • 比傳統(tǒng)的HashMap時間效率低。
  • 因為其會對key從小到大排序,使用二分法查詢key對應(yīng)在數(shù)組中的下標(biāo)。
  • 在添加、刪除、查找數(shù)據(jù)的時候都是先使用二分查找法得到相應(yīng)的index,然后通過index來進行添加、查找、刪除等操作。
  • 所以其是按照key的大小排序存儲的。

適用場景:

  • 數(shù)據(jù)量不大
  • 空間比時間重要
  • 需要使用Map
  • 在Android平臺,相對來說,內(nèi)存容量更寶貴。而且數(shù)據(jù)量不大。所以當(dāng)需要使用key是Object類型的Map時,可以考慮使用ArrayMap來替換HashMap

2 構(gòu)造方法

    //擴容默認的size, 4是相對效率較高的大小
    private static final int BASE_SIZE = 4;

    //表示集合是不可變的
    static final int[] EMPTY_IMMUTABLE_INTS = new int[0];
    //是否利用System.identityHashCode(key) 獲取唯一HashCode模式。    
    final boolean mIdentityHashCode;
    //保存hash值的數(shù)組
    int[] mHashes;
    //保存key/value的數(shù)組。
    Object[] mArray;
    //容量
    int mSize;
    //創(chuàng)建一個空的ArrayMap,默認容量是0.當(dāng)有Item被添加進來,會自動擴容
    public ArrayMap() {
        this(0, false);
    }
    //創(chuàng)建一個指定容量的ArrayMap
    public ArrayMap(int capacity) {
        this(capacity, false);
    }
    //指定容量和identityHashCode
    public ArrayMap(int capacity, boolean identityHashCode) {
        mIdentityHashCode = identityHashCode;
        //數(shù)量<  0,構(gòu)建一個不可變的ArrayMap
        if (capacity < 0) {
            mHashes = EMPTY_IMMUTABLE_INTS;
            mArray = EmptyArray.OBJECT;
            //數(shù)量= 0,構(gòu)建空的mHashes mArray
        } else if (capacity == 0) {
            mHashes = EmptyArray.INT;
            mArray = EmptyArray.OBJECT;
        } else {//數(shù)量>0,分配空間初始化數(shù)組
            allocArrays(capacity);
        }
        mSize = 0;
    }
    //利用另一個map構(gòu)建ArrayMap
    public ArrayMap(ArrayMap<K, V> map) {
        this();
        if (map != null) {
            putAll(map);
        }
    }

3 擴容allocArrays

        //擴容
        private void allocArrays(final int size) {
        //數(shù)量<  0,構(gòu)建一個不可變的ArrayMap
        if (mHashes == EMPTY_IMMUTABLE_INTS) {
            throw new UnsupportedOperationException("ArrayMap is immutable");
        }//擴容數(shù)量是 8
        if (size == (BASE_SIZE*2)) {
            synchronized (ArrayMap.class) {
                //查看之前是否有緩存的 容量為8的int[]數(shù)組和容量為16的object[]數(shù)組 
                //如果有,復(fù)用給mArray mHashes
                if (mTwiceBaseCache != null) {
                    final Object[] array = mTwiceBaseCache;
                    mArray = array;
                    mTwiceBaseCache = (Object[])array[0];
                    mHashes = (int[])array[1];
                    array[0] = array[1] = null;
                    mTwiceBaseCacheSize--;
                    if (DEBUG) Log.d(TAG, "Retrieving 2x cache " + mHashes
                            + " now have " + mTwiceBaseCacheSize + " entries");
                    return;
                }
            }
        } else if (size == BASE_SIZE) {//擴容數(shù)量是4
            synchronized (ArrayMap.class) {
                //查看之前是否有緩存的 容量為4的int[]數(shù)組和容量為8的object[]數(shù)組 
                //如果有,復(fù)用給mArray mHashes
                if (mBaseCache != null) {
                    final Object[] array = mBaseCache;
                    mArray = array;
                    mBaseCache = (Object[])array[0];
                    mHashes = (int[])array[1];
                    array[0] = array[1] = null;
                    mBaseCacheSize--;
                    if (DEBUG) Log.d(TAG, "Retrieving 1x cache " + mHashes
                            + " now have " + mBaseCacheSize + " entries");
                    return;
                }
            }
        }
        //構(gòu)建mHashes和mArray,mArray是mHashes的兩倍。因為它既要存key還要存value。
        mHashes = new int[size];
        mArray = new Object[size<<1];
    }
  • 擴容時,會查看之前是否有緩存的 int[]數(shù)組和object[]數(shù)組
  • 如果有,復(fù)用給mArray mHashes

4 增 、改

4.1 單個增改 put(K key, V value)

    //如果key存在,則返回oldValue
    public V put(K key, V value) {
        //key的hash值
        final int hash;
        //下標(biāo)
        int index;
        // 如果key為null,則hash值為0.
        if (key == null) {
            hash = 0;
            //尋找null的下標(biāo)
            index = indexOfNull();
        } else {
            //根據(jù)mIdentityHashCode 取到 hash值
            hash = mIdentityHashCode ? System.identityHashCode(key) : key.hashCode();
            //根據(jù)hash值和key 找到合適的index
            index = indexOf(key, hash);
        }
        //如果index>=0,說明是替換(改)操作
        if (index >= 0) {
            //只需要更新value 不需要更新key。因為key已經(jīng)存在
            index = (index<<1) + 1;
            //返回舊值
            final V old = (V)mArray[index];
            mArray[index] = value;
            return old;
        }
        //index<0,說明是插入操作。 對其取反,得到應(yīng)該插入的下標(biāo)
        index = ~index;
        //如果需要擴容
        if (mSize >= mHashes.length) {
            //如果容量大于8,則擴容一半。
            //否則容量大于4,則擴容到8.
            //否則擴容到4
            final int n = mSize >= (BASE_SIZE*2) ? (mSize+(mSize>>1))
                    : (mSize >= BASE_SIZE ? (BASE_SIZE*2) : BASE_SIZE);
            //臨時數(shù)組
            final int[] ohashes = mHashes;
            final Object[] oarray = mArray;
            //分配空間完成擴容
            allocArrays(n);
            //復(fù)制臨時數(shù)組中的數(shù)組進新數(shù)組
            if (mHashes.length > 0) {
                if (DEBUG) Log.d(TAG, "put: copy 0-" + mSize + " to 0");
                System.arraycopy(ohashes, 0, mHashes, 0, ohashes.length);
                System.arraycopy(oarray, 0, mArray, 0, oarray.length);
            }
            //釋放臨時數(shù)組空間
            freeArrays(ohashes, oarray, mSize);
        }
        //如果index在中間,則需要移動數(shù)組,騰出中間的位置
        if (index < mSize) {
            if (DEBUG) Log.d(TAG, "put: move " + index + "-" + (mSize-index)
                    + " to " + (index+1));
            System.arraycopy(mHashes, index, mHashes, index + 1, mSize - index);
            System.arraycopy(mArray, index << 1, mArray, (index + 1) << 1, (mSize - index) << 1);
        }
        //hash數(shù)組,就按照下標(biāo)存哈希值
        mHashes[index] = hash;
        //array數(shù)組,根據(jù)下標(biāo),乘以2存key,乘以2+1 存value
        mArray[index<<1] = key;
        mArray[(index<<1)+1] = value;
        mSize++;//修改size
        return null;
    }
    //返回key為null的 下標(biāo)index
    int indexOfNull() {
        //N為當(dāng)前集合size 
        final int N = mSize;
        //如果當(dāng)前集合是空的,返回~0
        if (N == 0) {//
            return ~0;
        }
        //根據(jù)hash值=0,通過二分查找,查找到目標(biāo)index
        int index = ContainerHelpers.binarySearch(mHashes, N, 0);
        //如果index《0,則hash值=0之前沒有存儲過數(shù)據(jù)
        if (index < 0) {
            return index;
        }
        //如果index>=0,說明該hash值,之前存儲過數(shù)據(jù),找到對應(yīng)的key,比對key是否等于null。相等的話,返回index。說明要替換。  
        //關(guān)于array中對應(yīng)數(shù)據(jù)的位置,是index*2 = key ,index*2+1 = value.
        if (null == mArray[index<<1]) {
            return index;
        }
        //以下兩個for循環(huán)是在出現(xiàn)hash沖突的情況下,找到正確的index的過程:
        //從index+1,遍歷到數(shù)組末尾,找到hash值相等,且key相等的位置,返回
        int end;
        for (end = index + 1; end < N && mHashes[end] == 0; end++) {
            if (null == mArray[end << 1]) return end;
        }
        //從index-1,遍歷到數(shù)組頭,找到hash值相等,且key相等的位置,返回
        for (int i = index - 1; i >= 0 && mHashes[i] == 0; i--) {
            if (null == mArray[i << 1]) return i;
        }
        // key沒有找到,返回一個負數(shù)。代表應(yīng)該插入的位置
        return ~end;
    }
    //根據(jù)key和key的hash值,返回index
    int indexOf(Object key, int hash) {
        //N為當(dāng)前集合size 
        final int N = mSize;
        //如果當(dāng)前集合是空的,返回~0
        if (N == 0) {
            return ~0;
        }
        //根據(jù)hash值,通過二分查找,查找到目標(biāo)index
        int index = ContainerHelpers.binarySearch(mHashes, N, hash);
        //如果index《0,說明該hash值之前沒有存儲過數(shù)據(jù)
        if (index < 0) {
            return index;
        }
        //如果index>=0,說明該hash值,之前存儲過數(shù)據(jù),找到對應(yīng)的key,比對key是否相等。相等的話,返回index。說明要替換。
        if (key.equals(mArray[index<<1])) {
            return index;
        }
        //以下兩個for循環(huán)是在出現(xiàn)hash沖突的情況下,找到正確的index的過程:
        //從index+1,遍歷到數(shù)組末尾,找到hash值相等,且key相等的位置,返回
        int end;
        for (end = index + 1; end < N && mHashes[end] == hash; end++) {
            if (key.equals(mArray[end << 1])) return end;
        }

        //從index-1,遍歷到數(shù)組頭,找到hash值相等,且key相等的位置,返回
        for (int i = index - 1; i >= 0 && mHashes[i] == hash; i--) {
            if (key.equals(mArray[i << 1])) return i;
        }
        // key沒有找到,返回一個負數(shù)。代表應(yīng)該插入的位置
        return ~end;
    }
  • 每次插入時,根據(jù)key的哈希值,利用二分查找,去尋找key在int[] mHashes數(shù)組中的下標(biāo)位置。
  • 如果出現(xiàn)了hash沖突,則從需要從目標(biāo)點向兩頭遍歷,找到正確的index。

4.2 批量增 putAll(ArrayMap<? extends K, ? extends V> array)

    //批量put方法:
    public void putAll(ArrayMap<? extends K, ? extends V> array) {
        final int N = array.mSize;
        //確??臻g足夠存放
        ensureCapacity(mSize + N);
        //如果當(dāng)前是空集合,
        if (mSize == 0) {
            if (N > 0) {//則直接復(fù)制覆蓋數(shù)組內(nèi)容即可。
                System.arraycopy(array.mHashes, 0, mHashes, 0, N);
                System.arraycopy(array.mArray, 0, mArray, 0, N<<1);
                mSize = N;
            }
        } else {//否則需要一個一個執(zhí)行插入put操作
            for (int i=0; i<N; i++) {
                put(array.keyAt(i), array.valueAt(i));
            }
        }
    }
    //確??臻g足夠存放 minimumCapacity 個數(shù)據(jù)
    public void ensureCapacity(int minimumCapacity) {
        //如果不夠擴容
        if (mHashes.length < minimumCapacity) {
            //暫存當(dāng)前的hash array。后面復(fù)制需要
            final int[] ohashes = mHashes;
            final Object[] oarray = mArray;
            //擴容空間(開頭講過這個函數(shù))
            allocArrays(minimumCapacity);
            if (mSize > 0) {//如果原集合不為空,復(fù)制原數(shù)據(jù)到新數(shù)組中
                System.arraycopy(ohashes, 0, mHashes, 0, mSize);
                System.arraycopy(oarray, 0, mArray, 0, mSize<<1);
            }
            //釋放回收臨時暫存數(shù)組空間
            freeArrays(ohashes, oarray, mSize);
        }
    }
    //釋放回收臨時暫存數(shù)組空間
    private static void freeArrays(final int[] hashes, final Object[] array, final int size) {
        //如果容量是8, 則將hashes 和array 緩存起來,以便下次使用
        if (hashes.length == (BASE_SIZE*2)) {
            synchronized (ArrayMap.class) {
                if (mTwiceBaseCacheSize < CACHE_SIZE) {
                    //0存,前一個緩存的cache
                    array[0] = mTwiceBaseCache;
                    //1 存 int[]數(shù)組
                    array[1] = hashes;
                    //2+ 元素置空 以便GC
                    for (int i=(size<<1)-1; i>=2; i--) {
                        array[i] = null;
                    }
                    //更新緩存引用為array
                    mTwiceBaseCache = array;
                    //增加緩存過的Array的數(shù)量
                    mTwiceBaseCacheSize++;
                    if (DEBUG) Log.d(TAG, "Storing 2x cache " + array
                            + " now have " + mTwiceBaseCacheSize + " entries");
                }
            }//相同邏輯,只不過緩存的是int[] 容量為4的數(shù)組 
        } else if (hashes.length == BASE_SIZE) {
            synchronized (ArrayMap.class) {
                if (mBaseCacheSize < CACHE_SIZE) {
                    array[0] = mBaseCache;
                    array[1] = hashes;
                    for (int i=(size<<1)-1; i>=2; i--) {
                        array[i] = null;
                    }
                    mBaseCache = array;
                    mBaseCacheSize++;
                    if (DEBUG) Log.d(TAG, "Storing 1x cache " + array
                            + " now have " + mBaseCacheSize + " entries");
                }
            }
        }
    }

4.3 小結(jié)

流程

  1. 先根據(jù)key得到hash值
  2. 根據(jù)hash值得到index
  3. 根據(jù)index正負,得知是插入還是替換
    3.1 如果是替換直接替換值即可
    3.2 如果是插入,先判斷是否需要擴容,并進行擴容 , 挪動數(shù)組位置,插入元素(類似ArrayList)
  • 插入允許key為null,value為null。

  • 每次插入時,根據(jù)key的哈希值,利用二分查找,去尋找key在int[] mHashes數(shù)組中的下標(biāo)位置。

  • 如果出現(xiàn)了hash沖突,則從需要從目標(biāo)點向兩頭遍歷,找到正確的index。

  • 根據(jù)key的hash值在mHashs中的index,如何得到key、value在mArray中的下標(biāo)位置呢?

    • key的位置是index2,value的位置是index2+1,也就是說mArray是利用連續(xù)的兩位空間去存放key、value。
  • 根據(jù)hash值的index計算,key、value的index也利用了位運算。index<<1 和 (index<<1)+1

5 刪

5.1 單個刪

    //如果對應(yīng)key有元素存在,返回value。否則返回null
    public V remove(Object key) {
        //根據(jù)key,找到下標(biāo)
        final int index = indexOfKey(key);
        if (index >= 0) {
            //如果index>=0,說明key有對應(yīng)的元素存在,則去根據(jù)下標(biāo)刪除
            return removeAt(index);
        }
        //否則返回null
        return null;
    }
    //根據(jù)下標(biāo)刪除元素
    public V removeAt(int index) {
        //根據(jù)index,得到value
        final Object old = mArray[(index << 1) + 1];
        //如果之前的集合長度小于等于1,則執(zhí)行過刪除操作后,集合現(xiàn)在就是空的了
        if (mSize <= 1) {
            // Now empty.
            if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to 0");
            //釋放回收空間
            freeArrays(mHashes, mArray, mSize);
            //置空
            mHashes = EmptyArray.INT;
            mArray = EmptyArray.OBJECT;
            mSize = 0;
        } else {//根據(jù)元素數(shù)量和集合占用的空間情況,判斷是否要執(zhí)行收縮操作
            //如果 mHashes長度大于8,且 集合長度 小于當(dāng)前空間的 1/3,則執(zhí)行一個 shrunk,收縮操作,避免空間的浪費
            if (mHashes.length > (BASE_SIZE*2) && mSize < mHashes.length/3) {
                // Shrunk enough to reduce size of arrays.  We dont allow it to
                // shrink smaller than (BASE_SIZE*2) to avoid flapping between
                // that and BASE_SIZE.
                //如果當(dāng)前集合長度大于8,則n為當(dāng)前集合長度的1.5倍。否則n為8.
                //n 為收縮后的 mHashes長度
                final int n = mSize > (BASE_SIZE*2) ? (mSize + (mSize>>1)) : (BASE_SIZE*2);

                if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to " + n);
                //分配新的更小的空間(收縮操作)
                final int[] ohashes = mHashes;
                final Object[] oarray = mArray;
                allocArrays(n);
                //刪掉一個元素,所以修改集合元素數(shù)量
                mSize--;
                //因為執(zhí)行了收縮操作,所以要將老數(shù)據(jù)復(fù)制到新數(shù)組中。
                if (index > 0) {
                    if (DEBUG) Log.d(TAG, "remove: copy from 0-" + index + " to 0");
                    System.arraycopy(ohashes, 0, mHashes, 0, index);
                    System.arraycopy(oarray, 0, mArray, 0, index << 1);
                }
                //在復(fù)制的過程中,排除不復(fù)制當(dāng)前要刪除的元素即可。
                if (index < mSize) {
                    if (DEBUG) Log.d(TAG, "remove: copy from " + (index+1) + "-" + mSize
                            + " to " + index);
                    System.arraycopy(ohashes, index + 1, mHashes, index, mSize - index);
                    System.arraycopy(oarray, (index + 1) << 1, mArray, index << 1,
                            (mSize - index) << 1);
                }
            } else {//不需要收縮
                //修改集合長度
                mSize--;
                //類似ArrayList,用復(fù)制操作去覆蓋元素達到刪除的目的。
                if (index < mSize) {
                    if (DEBUG) Log.d(TAG, "remove: move " + (index+1) + "-" + mSize
                            + " to " + index);
                    System.arraycopy(mHashes, index + 1, mHashes, index, mSize - index);
                    System.arraycopy(mArray, (index + 1) << 1, mArray, index << 1,
                            (mSize - index) << 1);
                }
                //記得置空,以防內(nèi)存泄漏
                mArray[mSize << 1] = null;
                mArray[(mSize << 1) + 1] = null;
            }
        }
        //返回刪除的值
        return (V)old;
    }

5.2 批量刪除

    //從ArrayMap中,刪除Collection集合中,所有出現(xiàn)的key。
    //返回值代表是否成功刪除元素
    public boolean removeAll(Collection<?> collection) {
        return MapCollections.removeAllHelper(this, collection);
    }
    //MapCollections.removeAllHelper(this, collection);
    //遍歷Collection,調(diào)用Map.remove(key)去刪除元素;
    public static <K, V> boolean removeAllHelper(Map<K, V> map, Collection<?> collection) {
        int oldSize = map.size();
        Iterator<?> it = collection.iterator();
        while (it.hasNext()) {
            map.remove(it.next());
        }
        //如果元素不等,說明成功刪除元素
        return oldSize != map.size();
    }
    //從ArrayMap中,刪除Collection集合中,所有出現(xiàn)的key。
    //返回值代表是否成功刪除元素
    public boolean removeAll(Collection<?> collection) {
        return MapCollections.removeAllHelper(this, collection);
    }
    //MapCollections.removeAllHelper(this, collection);
    //遍歷Collection,調(diào)用Map.remove(key)去刪除元素;
    public static <K, V> boolean removeAllHelper(Map<K, V> map, Collection<?> collection) {
        int oldSize = map.size();
        Iterator<?> it = collection.iterator();
        while (it.hasNext()) {
            map.remove(it.next());
        }
        //如果元素不等,說明成功刪除元素
        return oldSize != map.size();
    }
  • 根據(jù)元素數(shù)量和集合占用的空間情況,判斷是否要執(zhí)行收縮操作
  • 類似ArrayList,用復(fù)制操作去覆蓋元素達到刪除的目的。

6 查

  • 當(dāng)你想獲取某個value的時候,ArrayMap會計算輸入key轉(zhuǎn)換過后的hash值,然后對hash數(shù)組使用二分查找法尋找到對應(yīng)的index,然后我們可以通過這個index在另外一個數(shù)組中直接訪問到需要的鍵值對。
  • 如果在第二個數(shù)組鍵值對中的key和前面輸入的查詢key不一致,那么就認為是發(fā)生了碰撞沖突。為了解決這個問題,我們會以該key為中心點,分別上下展開,逐個去對比查找,直到找到匹配的值。如下圖所示:
    image

    隨著數(shù)組中的對象越來越多,查找訪問單個對象的花費也會跟著增長,這是在內(nèi)存占用與訪問時間之間做權(quán)衡交換。

6.1 單個查

    public V get(Object key) {
        //根據(jù)key去得到index
        final int index = indexOfKey(key);
        //根據(jù) index*2+1 得到value
        return index >= 0 ? (V)mArray[(index<<1)+1] : null;
    }
    public int indexOfKey(Object key) {
        //判斷key是否是null,并去查詢key對應(yīng)的index
        return key == null ? indexOfNull()
                : indexOf(key, mIdentityHashCode ? System.identityHashCode(key) : key.hashCode());
    }

7 總結(jié)

ArrayMap的實現(xiàn)細節(jié)很多地方和ArrayList很像

  • 每次插入時,根據(jù)key的哈希值,利用二分查找,去尋找key在int[] mHashes數(shù)組中的下標(biāo)位置。

  • 如果出現(xiàn)了hash沖突,則從需要從目標(biāo)點向兩頭遍歷,找到正確的index。

  • 擴容時,會查看之前是否有緩存的 int[]數(shù)組和object[]數(shù)組,如果有,復(fù)用給mArray mHashes

  • 擴容規(guī)則:如果容量大于8,則擴容一半。(類似ArrayList)

  • 根據(jù)key的hash值在mHashs中的index,如何得到key、value在mArray中的下標(biāo)位置呢?key的位置是index2,value的位置是index2+1,也就是說mArray是利用連續(xù)的兩位空間去存放key、value。

  • 根據(jù)元素數(shù)量和集合占用的空間情況,判斷是否要執(zhí)行收縮操作

  • 如果 mHashes長度大于8,且 集合長度 小于當(dāng)前空間的 1/3,則執(zhí)行一個 shrunk,收縮操作,避免空間的浪費(如果當(dāng)前集合長度大于8,則n為當(dāng)前集合長度的1.5倍。否則n為8.)

  • 用復(fù)制操作去覆蓋元素達到刪除的目的。

參考

ArrayMap與SparseArray源碼分析
面試必備:ArrayMap源碼解析

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,321評論 6 543
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,559評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,442評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,835評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,581評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,922評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,931評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,096評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,639評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,374評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,591評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,104評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,789評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,196評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,524評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,322評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,554評論 2 379

推薦閱讀更多精彩內(nèi)容