用SparseArray和ArrayMap代替HashMap

很早就听说这两个哥们了,并且也在项目中使用了,现在只是做一个总结
为什么google用这个代替Hashmap: 一般在数量是1000以内,可以考虑替代,HashMap底层是数组+哈希表+红黑树结构,默认的时候初始化一个16的数组来存储东西,每次扩充数组的时候都是原来数组的2倍,而且每次扩容都要重新计算数组的位置,不是直接copy();

  • 差异: HashMap实现了Serializable,ArrayMap并没有,所以数据传递的时候使用ArrayMap
  • SparseArray 在数组中仅有少部分的空间使用。因此造成内存空间的浪费,为了节省内存空间,并且不影响数组中原有的内容值,我们可以采用一种压缩的方式来表示稀疏数组的内容,去除了自动装箱.

1 SparseArray

1.1 简述

底层是稀疏数组的结构,key 值是int,所以他的hash值永远不会冲突,避免了对key的自动装箱(int转为Integer类型), 查找binarySearch二分查找,效率高,扩容时是直接copy的数组(不像hashmap一样去重新计算hash值,重新排列),基本上每次移除操作都要gc一下,回收无用的对象,它使用了两个数组,一个保存key,一个保存value,remove的时候不会立刻重新清理删除掉的数据,而是将对一个的数据标记为DELETE(一个Object对象)。在必要的环节调用gc清理标记为DELETE的空间

1.2 源码分析

  • 先看属性
// 要存的默认值
private static final Object DELETED = new Object();
private boolean mGarbage = false;
//存的key,不对外暴露
private int[] mKeys;
//存的value
private Object[] mValues;
//数组的大小(也就是数组真正的长度, 不是容量)
private int mSize;
复制代码
  • 构造方法
  1. 构造1
// 调用自己的构造方法2  默认容量是 10
 public SparseArray() {
        this(10);
    }
   
复制代码
  1. 构造2
 public SparseArray(int initialCapacity) {
        //如果传过来的是 0 
        if (initialCapacity == 0) {
         // key数组 赋值为0的长度 Values的数组也设为 0 
         // EmptyArray类中静态属性public static final int[] INT = new int[0];
         //public static final Object[] OBJECT = new Object[0];
            mKeys = EmptyArray.INT;
            mValues = EmptyArray.OBJECT;
        } else {
            // 初始化Object数组
            mValues = ArrayUtils.newUnpaddedObjectArray(initialCapacity);
            // 初始化key数组
            mKeys = new int[mValues.length];
        }
        // 初始化 存进去的个数
        mSize = 0;
    }
复制代码
  1. put()方法
  public void put(int key, E value) {
        // 做二分查找,如果存在 则返回 数组的索引, 不存在 i= ~i 取反的意思
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
        
        if (i >= 0) {
        // 表示存在,修改 value
            mValues[i] = value;
        } else {
        // 表示不存在, 再把 i 反回来(上面已经取反了,这里再取反,相当于 i = key) 
            i = ~i;
        // 此时i = key
            if (i < mSize && mValues[i] == DELETED) {
            // 如果 key 小于 数组length,并且 value对应值是否已删除就开始赋值
                mKeys[i] = key;
                mValues[i] = value;
                return;
            }
            
            
            if (mGarbage && mSize >= mKeys.length) {
            // 如果 数组length 大于 容量 就开始回收没用的对象
                gc();

                // 回收没用的对象之后,再次查找
                i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);
            }
            // 把key 插入到 key的数组中 并且扩容
            mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);
            // 把 value 查到 value的指定数组中
            mValues = GrowingArrayUtils.insert(mValues, mSize, i, value);
            //length + 1
            mSize++;
        }
    }
复制代码
  1. get() 方法
public E get(int key) {
        return get(key, null);
    }
复制代码
//通过二分查找, 查到就返回,没查到给默认值 也就是null
public E get(int key, E valueIfKeyNotFound) {
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
        //没有找到,或者已经删除返回null
        if (i < 0 || mValues[i] == DELETED) {
            return valueIfKeyNotFound;
        } else {
            return (E) mValues[i];
        }
    }
复制代码

ArrayMap

  • ArrayMap 也是implements Map,ArrayMap的存储中没有Entry这个东西,
  • 他是由两个数组来维护的,mHashes数组中保存的是每一项的HashCode值,mArray中就是键值对,每两个元素代表一个键值对,前面保存key,后面的保存value
  • 并没有实现Serializable,所以数据传递的时候使用ArrayMap
  • 通俗点来说, 它内部使用两个数组进行工作,其中一个数组记录key hash过后的顺序列表,另外一个数组按key的顺序记录Key-Value值
  • Bundle实际上就是一个ArrayMap, 以键值对的方式保存着各种类型的数据
  • 下面是ArrayMap官网的翻译
ArrayMap is a generic key->value mapping data structure that is
designed to be more memory efficient than a traditional {@link java.util.HashMap}.
It keeps its mappings in an array data structure -- an integer array of hash
codes for each item, and an Object array of the key/value pairs.  
This allows it to avoid having to create an extra object for every entry
put in to the map, and it also tries to control the growth of the size of these arrays more aggressively (since growing them only requires copying the entries in the array, not rebuilding a hash map).
</p>Note that this implementation is not intended to be appropriate for data structures that may contain large numbers of items. 
 It is generally slower than a traditional HashMap, since lookups require a binary search and adds and removes require inserting and deleting entries in the array.  
For containers holding up to hundreds of items,the performance difference is not significant, less than 50%.</p>
<p>Because this container is intended to better balance memory use, unlike most other standard Java containers it will shrink its array as items are removed from it.  
Currently you have no control over this shrinking -- if you set a capacity and then remove an item, it may reduce the capacity to better match the current size.  
In the future an explicit call to set the capacity should turn off this aggressive shrinking behavior
复制代码

ArrayMap 是一个通用的 key->value 映射关系的存储数据结构(容器),是为了比hashmap更高效的使用内存(也就是减少内存), mHashes数组中保存的是每一项的HashCode值,mArray中就是键值对,每两个元素代表一个键值对,前面保存key,后面的保存value.这样可以避免为每一个条目来创建额外的对象,它自己有效的控制数组的大小增长(因为扩容时只需要复制数组中的条目,而不是重建哈希映射关系)
需要注意的点是: 这种实现不可以存储大量的元素,因为arraymap里面用的是二分查找的方法来添加,删除,查找的.对于存储数百的数据,性能差异不显着,小于50%
因为这个容器旨在更好地平衡内存使用,与大多数其他标准Java容器不同,当项目从中移除时,它会收缩数组. 目前,你无法控制这种收缩——如果你设置一个容量,然后移除一个项目,它可能会降低更好地匹配当前大小的能力。在将来,设置容量的明确调用应该关闭这种积极的收缩行为。

  • 它是一种以时间换空间的优化,通常比HashMap要慢,因为在查找时需要进行二分查找,增加或删除时,需要在数组中插入或者删除键

源码分析

  1. 属性
复制代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值