面试必备:ArrayMap源码解析

本文详细解析了ArrayMap类的构造过程,特别是其容量管理和扩容机制,以及put操作的单个和批量实现,展示了HashMap的一个轻量级实现方式。
摘要由CSDN通过智能技术生成

输出:

onCreate() called with: map = [{null=2, 1=1, 3=null, 4=null, 5=null, 6=null}]

3 构造函数


//扩容默认的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值的数组

int[] mHashes;

//保存key/value的数组。

Object[] mArray;

//容量

int mSize;

//创建一个空的ArrayMap,默认容量是0.当有Item被添加进来,会自动扩容

public ArrayMap() {

this(0, false);

}

//创建一个指定容量的ArrayMap

public ArrayMap(int capacity) {

this(capacity, false);

}

//指定容量和identityHashCode

public ArrayMap(int capacity, boolean identityHashCode) {

mIdentityHashCode = identityHashCode;

//数量< 0,构建一个不可变的ArrayMap

if (capacity < 0) {

mHashes = EMPTY_IMMUTABLE_INTS;

mArray = EmptyArray.OBJECT;

//数量= 0,构建空的mHashes mArray

} else if (capacity == 0) {

mHashes = EmptyArray.INT;

mArray = EmptyArray.OBJECT;

} else {//数量>0,分配空间初始化数组

allocArrays(capacity);

}

mSize = 0;

}

//扩容

private void allocArrays(final int size) {

//数量< 0,构建一个不可变的ArrayMap

if (mHashes == EMPTY_IMMUTABLE_INTS) {

throw new UnsupportedOperationException(“ArrayMap is immutable”);

}//扩容数量是 8

if (size == (BASE_SIZE*2)) {

synchronized (ArrayMap.class) {

//查看之前是否有缓存的 容量为8的int[]数组和容量为16的object[]数组

//如果有,复用给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) {//扩容数量是4

synchronized (ArrayMap.class) {

//查看之前是否有缓存的 容量为4的int[]数组和容量为8的object[]数组

//如果有,复用给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;

}

}

}

//构建mHashes和mArray,mArray是mHashes的两倍。因为它既要存key还要存value。

mHashes = new int[size];

mArray = new Object[size<<1];

}

//利用另一个map构建ArrayMap

public ArrayMap(ArrayMap<K, V> map) {

this();

if (map != null) {

putAll(map);

}

}

//批量put方法:

public void putAll(ArrayMap<? extends K, ? extends V> array) {

final int N = array.mSize;

//确保空间足够存放

ensureCapacity(mSize + N);

//如果当前是空集合,

if (mSize == 0) {

if (N > 0) {//则直接复制覆盖数组内容即可。

System.arraycopy(array.mHashes, 0, mHashes, 0, N);

System.arraycopy(array.mArray, 0, mArray, 0, N<<1);

mSize = N;

}

} else {//否则需要一个一个执行插入put操作

for (int i=0; i<N; i++) {

put(array.keyAt(i), array.valueAt(i));

}

}

}

//确保空间足够存放 minimumCapacity 个数据

public void ensureCapacity(int minimumCapacity) {

//如果不够扩容

if (mHashes.length < minimumCapacity) {

//暂存当前的hash array。后面复制需要

final int[] ohashes = mHashes;

final Object[] oarray = mArray;

//扩容空间(开头讲过这个函数)

allocArrays(minimumCapacity);

if (mSize > 0) {//如果原集合不为空,复制原数据到新数组中

System.arraycopy(ohashes, 0, mHashes, 0, mSize);

System.arraycopy(oarray, 0, mArray, 0, mSize<<1);

}

//释放回收临时暂存数组空间

freeArrays(ohashes, oarray, mSize);

}

}

//释放回收临时暂存数组空间

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[]数组

array[1] = hashes;

//2+ 元素置空 以便GC

for (int i=(size<<1)-1; i>=2; i–) {

array[i] = null;

}

//更新缓存引用为array

mTwiceBaseCache = array;

//增加缓存过的Array的数量

mTwiceBaseCacheSize++;

if (DEBUG) Log.d(TAG, "Storing 2x cache " + array

  • " now have " + mTwiceBaseCacheSize + " entries");

}

}//相同逻辑,只不过缓存的是int[] 容量为4的数组

} 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");

}

}

}

}

小结:

* 扩容时,会查看之前是否有缓存的 int[]数组和object[]数组

* 如果有,复用给mArray mHashes

4 增 、改


4.1 单个增改 put(K key, V value)

//如果key存在,则返回oldValue

public V put(K key, V value) {

//key的hash值

final int hash;

//下标

int index;

// 如果key为null,则hash值为0.

if (key == null) {

hash = 0;

//寻找null的下标

index = indexOfNull();

} else {

//根据mIdentityHashCode 取到 hash值

hash = mIdentityHashCode ? System.identityHashCode(key) : key.hashCode();

//根据hash值和key 找到合适的index

index = indexOf(key, hash);

}

//如果index>=0,说明是替换(改)操作

if (index >= 0) {

//只需要更新value 不需要更新key。因为key已经存在

index = (index<<1) + 1;

//返回旧值

final V old = (V)mArray[index];

mArray[index] = value;

return old;

}

//index<0,说明是插入操作。 对其取反,得到应该插入的下标

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);

//临时数组

final int[] ohashes = mHashes;

final Object[] oarray = mArray;

//分配空间完成扩容

allocArrays(n);

//复制临时数组中的数组进新数组

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);

}

//释放临时数组空间

freeArrays(ohashes, oarray, mSize);

}

//如果index在中间,则需要移动数组,腾出中间的位置

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数组,就按照下标存哈希值

mHashes[index] = hash;

//array数组,根据下标,乘以2存key,乘以2+1 存value

mArray[index<<1] = key;

mArray[(index<<1)+1] = value;

mSize++;//修改size

return null;

}

//返回key为null的 下标index

int indexOfNull() {

//N为当前集合size

final int N = mSize;

//如果当前集合是空的,返回~0

if (N == 0) {//

return ~0;

}

//根据hash值=0,通过二分查找,查找到目标index

int index = ContainerHelpers.binarySearch(mHashes, N, 0);

//如果index《0,则hash值=0之前没有存储过数据

if (index < 0) {

return index;

}

//如果index>=0,说明该hash值,之前存储过数据,找到对应的key,比对key是否等于null。相等的话,返回index。说明要替换。

//关于array中对应数据的位置,是index2 = key ,index2+1 = value.

if (null == mArray[index<<1]) {

return index;

}

//以下两个for循环是在出现hash冲突的情况下,找到正确的index的过程:

//从index+1,遍历到数组末尾,找到hash值相等,且key相等的位置,返回

int end;

for (end = index + 1; end < N && mHashes[end] == 0; end++) {

if (null == mArray[end << 1]) return end;

}

//从index-1,遍历到数组头,找到hash值相等,且key相等的位置,返回

for (int i = index - 1; i >= 0 && mHashes[i] == 0; i–) {

if (null == mArray[i << 1]) return i;

}

// key没有找到,返回一个负数。代表应该插入的位置

return ~end;

}

//根据key和key的hash值,返回index

int indexOf(Object key, int hash) {

//N为当前集合size

final int N = mSize;

//如果当前集合是空的,返回~0

if (N == 0) {

return ~0;

}

//根据hash值,通过二分查找,查找到目标index

int index = ContainerHelpers.binarySearch(mHashes, N, hash);

//如果index《0,说明该hash值之前没有存储过数据

if (index < 0) {

return index;

}

//如果index>=0,说明该hash值,之前存储过数据,找到对应的key,比对key是否相等。相等的话,返回index。说明要替换。

if (key.equals(mArray[index<<1])) {

return index;

}

//以下两个for循环是在出现hash冲突的情况下,找到正确的index的过程:

//从index+1,遍历到数组末尾,找到hash值相等,且key相等的位置,返回

int end;

for (end = index + 1; end < N && mHashes[end] == hash; end++) {

if (key.equals(mArray[end << 1])) return end;

}

//从index-1,遍历到数组头,找到hash值相等,且key相等的位置,返回

for (int i = index - 1; i >= 0 && mHashes[i] == hash; i–) {

if (key.equals(mArray[i << 1])) return i;

}

// key没有找到,返回一个负数。代表应该插入的位置

return ~end;

}

4.2 批量增 putAll(Map

//批量增,接受更为广泛的Map参数

public void putAll(Map<? extends K, ? extends V> map) {

//确保空间容量足够

ensureCapacity(mSize + map.size());

for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {

//分别调用单个增方法 add

put(entry.getKey(), entry.getValue());

}

}

文末

好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划,可以来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。

这里放上一部分我工作以来以及参与过的大大小小的面试收集总结出来的一套进阶学习的视频及面试专题资料包,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

(Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {

//分别调用单个增方法 add

put(entry.getKey(), entry.getValue());

}

}

文末

好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划,可以来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。

这里放上一部分我工作以来以及参与过的大大小小的面试收集总结出来的一套进阶学习的视频及面试专题资料包,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家

[外链图片转存中…(img-jlDUIqjH-1714725223645)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值