SparseArray到底哪点比HashMap好
SparseArray是android里为<Interger,Object>这样的Hashmap而专门写的class,目的是提高效率,其核心是折半查找函数(binarySearch)。
HashMap底层是一个Hash表,是数组和链表的集合实现,有需要的可以去看看我关于Hashmap的分析。hashmap源码分析
所以Android开发中官方推荐:当使用HashMap(K, V),如果K为整数类型时,使用SparseArray的效率更高。
那我们看源码来分析下,
构造函数:
/**
* 存储索引集合.
*/
private int[] mKeys;
/**
* 存储对象集合.
*/
private Object[] mValues;
/**
* 存储的键值对总数.
*/
private int mSize;
/**
* 采用默认的构造函数,则初始容量为10.
*/
public SparseArray() {
this(10);
}
/**
* 使用指定的初始容量构造SparseArray.
*
* @param initialCapacity 初始容量
*/
public SparseArray(int initialCapacity) {
if (initialCapacity == 0) {
// Effective Java中第43条:返回零长度的数组或者集合,而不是:null
mKeys = ContainerHelpers.EMPTY_INTS;
mValues = ContainerHelpers.EMPTY_OBJECTS;
} else {
// 构造initialCapacity大小的int数组和object数组
mKeys = new int[initialCapacity];
mValues = new Object[initialCapacity];
}
// 设置SparseArray存储的<key,value>键值对个数为0.
mSize = 0;
}
和HashMap的数据结构不同,HashMap是使用数组+链表的数据结构存储键值对,而SparseArray只是用了两个数组进行存储。
我们知道链表的时间复杂度是很高的,这估计也是造成hashmap时间复杂度高的一个原因。
ContainerHelpers
ContainerHelpers类提供了二分查找算法,这也一定程度上提高了查找的效率
<span style="font-size:12px;">class ContainerHelpers {
// This is Arrays.binarySearch(), but doesn't do any argument validation.
static int binarySearch(int[] array, int size, int value) {
// 获取二分的起始和结束下标.
int lo = 0;
int hi = size - 1;
while (lo <= hi) {
// 获取中点的下标和值
final int mid = (lo + hi) >>> 1;
final int midVal = array[mid];
if (midVal < value) {
lo = mid + 1;
} else if (midVal > value) {
hi = mid - 1;
} else {
return mid; // value found
}
}
return ~lo; // value not present
}
}</span>
put()函数
/**
* 在SparseArray中存储键值对.
*/
public void put(int key, E value) {
// 通过二分查找算法计算索引
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
if (i >= 0) {
// key已经存在对应的value,则直接替换value.
mValues[i] = value;
} else {
i = ~i;
if (i < mSize && mValues[i] == DELETED) {
// 特殊的case,直接存储key-value即可
mKeys[i] = key;
mValues[i] = value;
return;
}
if (mGarbage && mSize >= mKeys.length) {
// 如果有元素被删除,并且目前容量不足,先进行一次gc
gc();
// Search again because indices may have changed.
i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);
}
// 扩容
if (mSize >= mKeys.length) {
// 获取扩容的数组大小
int n = mSize + 1;
int[] nkeys = new int[n];
Object[] nvalues = new Object[n];
// 数组拷贝最好使用System.arraycopy,而不是自己重撸一遍
System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
mKeys = nkeys;
mValues = nvalues;
}
// i为插入位置,如果i<mSize,则i之后的元素需要依次向后移动一位.
if (mSize - i != 0) {
System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
}
// 设置值,存储数量+1
mKeys[i] = key;
mValues[i] = value;
mSize++;
}
}
put函数的逻辑:
- 通过二分查找算法,计算key的索引值.
- 如果索引值大于0,说明有key对应的value存在,直接替换value即可.
- 如果索引值小于0,对索引值取反,获取key应该插入的坐标i.
- 判断是否需要扩容:1.需要扩容,则先扩容; 2.不需要扩容,则利用System.arraycopy移动相应的元素,进行(key,value)键值对插入.
get()函数
get函数就是利用二分查找获取key的下标,然后从object[] value数组中根据下标获取值.
之所以SparseArray号称比HashMap有更好的性能:
- SparseArray更加节约内存,一个int[]数组存储所有的key,一个object[] 数组存储所有的value.
- HashMap遇到冲突时,时间复杂度为O(n).而SparseArray不会有冲突,采用二分搜索算法,时间复杂度为O(lgn).
/**
* 根据指定的key获取value.
*/
public E get(int key) {
return get(key, null);
}
/**
* 利用二分查找算法根据key获取指定的value.
*/
public E get(int key, E valueIfKeyNotFound) {
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
if (i < 0 || mValues[i] == DELETED) {
return valueIfKeyNotFound;
} else {
return (E) mValues[i];
}
}
delete()函数
/**
* 根据key删除指定的value.
*/
public void delete(int key) {
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
if (i >= 0) {
if (mValues[i] != DELETED) {
// 标记i的值为private static final Object DELETED = new Object();
mValues[i] = DELETED;
// 设置gc标记为true.
mGarbage = true;
}
}
}
/**
* Alias for {@link #delete(int)}.
*/
public void remove(int key) {
delete(key);
}
gc()函数
if (mGarbage && mSize >= mKeys.length) {
// 如果有元素被删除,并且目前容量不足,先进行一次gc
gc();
// Search again because indices may have changed.
i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);
}
通过上面的源码分析,我们不难得出:
SparseArray到底哪点比HashMap好的更多相关文章
- 关于Android中ArrayMap/SparseArray比HashMap性能好的深入研究
由于网上有朋友对于这个问题已经有了很详细的研究,所以我就不班门弄斧了: 转载于:http://android-performance.com/android/2014/02/10/android-sp ...
- Android内存优化(使用SparseArray和ArrayMap代替HashMap)
在Android开发时,我们使用的大部分都是Java的api,比如HashMap这个api,使用率非常高,但是对于Android这种对内存非常敏感的移动平台,很多时候使用一些java的api并不能达到 ...
- 数据结构HashMap(Android SparseArray 和ArrayMap)
HashMap也是我们使用非常多的Collection,它是基于哈希表的 Map 接口的实现,以key-value的形式存在.在HashMap中,key-value总是会当做一个整体来处理,系统会根据 ...
- 【转】HashMap,ArrayMap,SparseArray源码分析及性能对比
HashMap,ArrayMap,SparseArray源码分析及性能对比 jjlanbupt 关注 2016.06.03 20:19* 字数 2165 阅读 7967评论 13喜欢 43 Array ...
- Android内存优化(使用SparseArray和ArrayMap取代HashMap)
在Android开发时,我们使用的大部分都是Java的api,比方HashMap这个api,使用率非常高,可是对于Android这样的对内存非常敏感的移动平台,非常多时候使用一些java的api并不能 ...
- Android学习笔记之性能优化SparseArray
PS:终于考完试了.来一发.微机原理充满了危机.不过好在数据库89分,还是非常欣慰的. 学习内容: 1.Android中SparseArray的使用.. 昨天研究完横向二级菜单,发现其中使用了Sp ...
- android小知识之SparseArray(HaspMap替换)
最近编程时,发现一个针对HashMap<Integer, E>的一个提示: 翻译过来就是:用SparseArray<E>来代替会有更好性能.那我们就来看看源码中SparseAr ...
- Android编程之SparseArray<E>详解
最近编程时,发现一个针对HashMap<Integer, E>的一个提示: 翻译过来就是:用SparseArray<E>来代替会有更好性能.那我们就来看看源码中SparseAr ...
- Android 性能优化 SparseArray【转载】
原文地址:Android学习笔记之性能优化SparseArray 学习内容: 1.Android中SparseArray的使用.. 昨天研究完横向二级菜单,发现其中使用了SparseArray去替 ...
随机推荐
- JVM初探- 内存分配、GC原理与垃圾收集器
JVM初探- 内存分配.GC原理与垃圾收集器 标签 : JVM JVM内存的分配与回收大致可分为如下4个步骤: 何时分配 -> 怎样分配 -> 何时回收 -> 怎样回收. 除了在概念 ...
- Dynamics CRM2016 Web API之删除单个查找字段值
之前的博文中有介绍过,Web Api中的一个删除单个属性的Api但没提供查找字段的删除方法,本篇补充上,这里给出的示例代码是C#的(主要看url的拼接),看下url中最后的/$ref,这个标示表明了当 ...
- Android简易实战教程--第四十九话《满屏拖动的控件》
今天做个有意思的效果吧,控件的拖拽,简单实用,逻辑清晰点3分钟看完. 说的很高大上,其实就是拖动Button按钮跟着鼠标位置满手机屏幕跑罢了. 直接上简单的代码吧: public class Main ...
- Effective Python 中文版
如题,博主正在翻译一本Python相关的书. 图为Python作者. [美]Brett Slatkin的名作. Effective Python: 59 Specific Ways to Write ...
- Dynamics CRM2016 Web API之创建记录
前篇介绍了通过primary key来查询记录,那query的知识点里面还有很多需要学习的,这个有待后面挖掘,本篇来简单介绍下用web api的创建记录. 直接上代码,这里的entity的属性我列了几 ...
- 微信小程序基础之创建使用教程
本文档将带你一步步创建完成一个微信小程序,并可以在手机上体验该小程序的实际效果.这个小程序的首页将会显示欢迎语以及当前用户的微信头像,点击头像,可以在新开的页面中查看当前小程序的启动日志. 1. 获取 ...
- 手动添加SSH支持、使用c3p0
之前做的笔记,现在整理一下:大家有耐心的跟着做就能成功: SSH(struts2.spring.hibernate) * struts2 * 充当mvc的角色 * hibernate ...
- 有两个序列a,b,大小都为n,序列元素的值是任意整数,无序。
要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b元素的和]之间的差最小. 例如: var a=[100,99,98,1,2, 3]; var b=[1, 2, 3, 4,5,40]. in ...
- FFmpeg源代码简单分析:avformat_write_header()
===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...
- Android简易实战教程--第十五话《在外部存储中读写文件》
第七话里面介绍了在内部存储读写文件 点击打开链接. 这样有一个比较打的问题,假设系统内存不够用,杀本应用无法执行,或者本应用被用户卸载重新安装后.以前保存的用户名和密码都不会得到回显.所以,有必要注意 ...