为你的Android程序选择合适的数据容器
2017-08-18 15:20
267 查看
转载请注明出处:http://blog.csdn.net/hjf_huangjinfu/article/details/77334432
程序需要数据、而数据需要容器。为数据选择一个合适的容器,可以提高程序的质量。平台和框架已经为我们准备了大量的数据容器,本文初步预览一下各容器的特性和使用场景。一些特殊业务相关的数据容器,需要开发者自行实现,或者组合现有容器,或者重新编写。选择容器类别
1、List
使用场景:需要存放一组元素,随后可以以任意顺序读取数据,并且可以允许同一个容器中有相同的元素。2、Set
使用场景:需要存放一组元素,随后可以以任意顺序读取数据,但是不允许同一个容器中有相同的元素时。3、Map
使用场景:需要存放一组键值对。4、Queue
使用场景:需要存放一组数据,但是只能以某种约定的顺序读取数据、或者一个支持生产者-消费者模型的容器。5、Stack、Deque
使用场景:需要存放一组数据,随后以后进先出(LIFO)的顺序读取数据。选择一个List
1、ArrayList
使用场景:不需要支持多线程,按索引读取、更新的操作比较多,而插入和删除的操作比较少。
实现原理:
数据存储:数组,删除数据需要移动数组中的其他数据。
2、LinkedList
使用场景:不需要支持多线程,但是经常删除和插入数据,很少进行读取操作。
实现原理:
数据存储:双向链表。
3、Vector
使用场景:希望支持多线程。
实现原理:
数据存储:数组。
线程安全:采用Java监视器模式,每个public方法都使用Vector自身的锁来保护。
4、CopyOnWriteArrayList
使用场景:希望支持多线程,而且比 Vector 有更好的可伸缩性,尤其是读取线程数量远大于写入线程数量。
实现原理:
数据存储:数组。
并发方案:写操作的方法都使用CopyOnWriteArrayList自身的锁进行保护,然后在原有数据副本(volatile修饰)上面进行修改,修改完成后用该副本替换原有数据。读操作的方法都是非同步方法。写时加锁复制,读时不加锁。
选择一个Set
1、HashSet
使用场景:不需要支持多线程,最普通的Set,没有其他额外需求。
实现原理:
数据存储:内部委托给HashMap,忽略Map的值。
2、ArraySet
使用场景:不需要支持多线程,对数据顺序没有需求,但是希望跟HashSet相比,能够牺牲一些时间来换取更高的内存使用率(毕竟是移动平台)。
实现原理:
数据存储:双数组(hash数组+数据数组),hash数组用来有序存储数据的hash,使用二叉查找来定位,对象数组存储数据,和hash在同一个索引。
备注:
Android平台提供,不属于JDK。
3、TreeSet
使用场景:不需要支持多线程,但是希望Set中的数据能够以某种约定的顺序排列,以便后面在遍历的时候使用,而且希望能按规则生成一些有序的子Set。
实现原理:
数据存储:内部委托给TreeMap,忽略Map的值。
4、LinkedHashSet
使用场景:不需要支持多线程,但是希望能保存数据插入时的顺序,以便后面在遍历的时候使用。
实现原理:
数据存储:内部委托给LinkedHashMap,忽略Map的值。
5、EnumSet
使用场景:不需要支持多线程,但是希望只存储一个enum类型的所有值,而且集合构建后不会改变,并且希望在时间和空间上都比较高效。
实现原理:
数据存储:基于数组,内部数据构造由JVM提供。
6、CopyOnWriteArraySet
使用场景:需要支持多线程,而且对数据排序没有任何要求,尤其是读取线程数量远大于写入线程数量。
实现原理:
数据存储:内部委托给CopyOnWriteArrayList。
并发方案:内部委托给CopyOnWriteArrayList。
7、ConcurrentSkipListSet
使用场景:需要支持多线程,但是希望Set中的数据能够以某种约定的顺序排列,以便后面在遍历的时候使用,而且希望能按规则生成一些有序的子Set。
实现原理:
数据存储:内部委托给ConcurrentSkipListMap。
并发方案:内部委托给ConcurrentSkipListMap。
选择一个Map
1、HashMap
使用场景:不需要支持多线程,最普通的Map,没有其他额外需求。
实现原理:
数据存储:基于散列表(Hash表),使用拉链法作为hash冲突解决方案。
2、ArrayMap
使用场景:不需要支持多线程,对数据顺序没有需求,但是希望跟HashMap相比,能够牺牲一些时间来换取更高的内存使用率(毕竟是移动平台)。
实现原理:
数据存储:双数组(hash数组 + Key-Value数组)K-V数组长度是hash数组长度的2倍,hash数组用来有序存储数据的hash,使用二叉查找来定位,如果Key的hash所在的hash数组的索引为i,那么Key和Value在K-V数组的索引分别为 i*2,i*2+1,使用开放地址法作为hash冲突解决方案。
备注:
Android平台提供,不属于JDK。
3、TreeMap
使用场景:不需要支持多线程,但是希望数据能按照给定的Key的顺序进行排列,并希望后面可以按规则生成一些Key有序的子Map。
实现原理:
数据存储:基于红黑树。
4、LinkedHashMap
使用场景:不需要支持多线程,但是希望能保存数据插入时的顺序,以便后面在遍历的时候使用。
实现原理:
数据存储:基于散列表(继承自HashMap),使用一个额外的双链表来存储数据插入的顺序。
5、Hashtable
使用场景:希望支持多线程,对并发性能要求不高。
实现原理:
数据存储:基于散列表,使用拉链法作为hash冲突解决方案。
线程安全:采用Java监视器模式,每个public方法都使用Hashtable自身的锁来保护。
6、WeakHashMap
使用场景:不需要支持多线程,但是希望GC可以回收掉一些外部不再使用的键值对(除了该Map,没有地方持有对Key的引用)。
实现原理:
数据存储:基于散列表,使用拉链法作为hash冲突解决方案,使用WeakReference来持有对Key的引用。
7、EnumMap
使用场景:不需要支持多线程,但是Key全是某一个enum类型的值,并且希望在时间和空间上都比较高效。
实现原理:
数据存储:基于双数组(enum数组+数据数组),内部数据构造由JVM提供。
8、IdentityHashMap
使用场景:不需要支持多线程,但是对Key的识别更为严格,只有引用相同(a == b = true)的Key才被认为是同一个Key。
实现原理:
数据存储:基于数组,因为不使用数据的hash,所以不存在hash冲突。
9、ConcurrentHashMap
使用场景:需要支持多线程,而且对并发性能要求比较高,但是对数据顺序没有要求。
实现原理:
数据存储:基于散列表(Hash表),使用拉链法作为hash冲突解决方案。
并发方案:主要基于锁分段技术,每一个在散列桶首部的数据作为整个散列桶的锁,写时加锁,读时不加锁。
10、ConcurrentSkipListMap
使用场景:需要支持多线程,对并发性能要求比较高,并且希望数据能按照给定的Key的顺序进行排列,并希望后面可以按规则生成一些Key有序的子Map。
实现原理:
数据存储:基于跳跃表(SkipList)。
并发方案:基于CAS技术。
11、SparseArray、SparseBooleanArray、SparseIntArray、SparseLongArray
使用场景:不需要支持多线程,但是Key为int类型,并且希望能够牺牲一些时间来换取更高的内存使用率(毕竟是移动平台)。
实现原理:
数据存储:基于双数组(int类型的key数组 + 数组数组),key在数组中有序排列,使用二叉查找进行定位。
备注:SparseArray、SparseBooleanArray、SparseIntArray、SparseLongArray 的Value类型分别为 Object、boolean、int、long。
备注:
Android平台提供,不属于JDK。
选择一个Queue
1、LinkedList、ArrayDeque
使用场景:不需要支持多线程的,先进先出,简单的队列。
使用比较:
LinkedList支持null数据,而ArrayDeque不支持null数据。
实现原理:
LinkedList:基于双向链表。
ArrayDeque:基于循环数组。
2、LinkedBlockingQueue、ArrayBlockingQueue
使用场景:需要支持多线程,先进先出,并且可阻塞的生产者-消费者队列。
使用比较:
LinkedBlockingQueue支持无界队列和有界队列两种模式,ArrayBlockingQueue只有有界模式,其实如果把界限设置为Integer.MAX_VALUE,在有界无界这一方面就没区别,但是性能上面就差很多了,ArrayBlockingQueue需要一次性分配很多内存,而LinkedBlockingQueue是逐渐分配内存,但是在时间性能上面,ArrayBlockingQueue要比LinkedBlockingQueue好。对于被阻塞的线程来说ArrayBlockingQueue支持公平模式,而LinkedBlockingQueue只支持非公平模式。
实现原理:
LinkedBlockingQueue:数据存储基于单向链表,并发方案:基于"读"、"写"两个锁和"非空"、"非满"两个条件队列。
ArrayBlockingQueue:数据存储:基于循环数组,并发方案:基于ReentrantLock和"非空"、"非满"两个条件队列。
3、ConcurrentLinkedDeque、ConcurrentLinkedQueue
使用场景:需要支持多线程、先进先出,非阻塞的,无界的生产者-消费者队列,并且对高并发的性能有要求。
使用比较:
ConcurrentLinkedDeque支持双向队列,而ConcurrentLinkedQueue只支持单向队列。
实现原理:
数据存储:ConcurrentLinkedDeque基于双向链表,ConcurrentLinkedQueue基于单向链表。
并发方案:基于CAS技术。
4、PriorityQueue、PriorityBlockingQueue
使用场景:需要一个无界的,以自定义的顺序来读取数据,而不是先进先出。
使用比较:
PriorityQueue不支持多线程,也不支持阻塞,而PriorityBlockingQueue既支持多线程,也支持阻塞。
实现原理:
数据存储:基于二叉堆。
并发方案:PriorityBlockingQueue的并发方案使用ReentrantLock和"非空"条件队列。
5、SynchronousQueue、LinkedTransferQueue
使用场景:需要一个先进先出的,有强转移控制权的生产者-消费者队列,一个生产者线程向队列添加数据,必须有一个消费者线程显式的取走该数据,生产者线程才能返回。
使用比较:
SynchronousQueue只支持强移交控制权模式,而LinkedTransferQueue支持强控制权和普通两种模式。
实现原理:
SynchronousQueue:基于双队列(生产者线程队列 + 消费者线程队列),没有数据存储空间,线程的调度使用LockSupport,配合CAS来操作链表。
LinkedTransferQueue:数据存储基于单向链表,并发阻塞控制方案:LockSupport + CAS。
6、LinkedBlockingDeque
使用场景:需要支持多线程,可阻塞的,双向队列。
实现原理:
数据存储:双向链表。
并发阻塞控制方案:基于单个ReentrantLock和"非空"、"非满"两个条件队列。
7、DelayQueue
使用场景:需要一个数据读取顺序依赖于特定延时时间的队列。
实现原理:
数据存储:内部委托给PriorityQueue。
排序规则:自定义排序规则,但是必须依赖于延时时间。
选择一个Stack、Deque
1、ArrayDeque、LinkedList
使用场景:不需要支持多线程,简单的栈。
实现原理:
数据存储:基于循环数组。
2、Stack
使用场景:需要支持多线程。
实现原理:
数据存储:内部委托给Vector(继承自Vector)。
线程安全:内部委托给Vector。
3、LinkedBlockingDeque
使用场景:需要支持多线程,可阻塞的,双向队列。
实现原理:
数据存储:双向链表。
并发阻塞控制方案:基于单个ReentrantLock和"非空"、"非满"两个条件队列。
4、ConcurrentLinkedDeque
使用场景:需要支持多线程、先进先出,非阻塞的,无界的生产者-消费者队列,并且对高并发的性能有要求。
实现原理:
数据存储:基于双向链表。
并发方案:基于CAS技术。
附录
1、SkipList,一个以链表形式实现有序顺序表,参考链接:https://en.wikipedia.org/wiki/Skip_list2、红黑树,一个自平衡二叉树,参考链接:https://en.wikipedia.org/wiki/Red%E2%80%93black_tree
3、CAS,一个非阻塞算法,参考链接:https://en.wikipedia.org/wiki/Compare-and-swap
4、锁分段技术,一个降低锁竞争的方法,参考链接:https://en.wikipedia.org/wiki/Lock_(computer_science)#Granularity
相关文章推荐
- 为你的PHP程序选择合适的密码库(初稿)
- 学习笔记_android之选择合适的数据存储方式
- 为你的PHP程序选择合适的密码库(初稿)
- 国际:程序员,如何选择合适的程序语言
- 程序员,如何选择合适的程序语言
- 程序员,如何选择合适的程序语言
- 程序员,如何选择合适的程序语言
- 选择合适的数据控件
- Android: 如何创建AVD以及选择合适target类型
- 选择合适的数据类型
- 如何选择合适的容器以及其实现品
- Android 用sharepreference在各个程序之间共享数据
- 程序代码段:DataTable排序-checkListBox绑定数据表,获取值,选择等操作。
- Mysql 选择合适的数据类型
- 程序员要选择合适自己的程序语言
- 选择合适的数据控件
- 仔细选择合适的STL容器
- (WebSite----Asp.Net Configuration----->无法连接到SQL Server数据库------>选择数据存储区---->应用程序当前被配置为使用提供程序:AspNetSqlProvider)解决方案
- 如何选择合适的加密技术保障数据安全
- 选择合适的数据控件