Android中基于java数据结构的实现

前面我们已经了解了java中的九大数据结构,那么它们在Android中是怎么被实现的呢。

Collection

List(可存放重复元素,有序的)

  • ArrayList(基于数组
    是最常用的一种数据结构。内部是基于数组实现的。线程不安全。

    大家应该知道数组是无法扩容的,申明了多大的就是多大的长度。而且数组的插入和删除比较慢,需要将该位置之后的元素集体前移或后移。

    ArrayList的主要功能就是解决这两个缺点。
    核心实现原理:扩容会涉及到新建数组、元素复制等
    假如现有一个ArrayList对象,内部数组长度为10,现需要在第5个位置新增一个元素,则步骤如下:
    1,在内存中新建一个长度为15(每次扩容是在原有长度上增加该长度的一半)的新数组
    2,将原来数组中的所有元素复制到新数组中
    3,将新数组中5-10位置的元素复制到6-11位置中
    4,将第5个位置的元素修改为新增加的值

  • LinkedList(基于链表-双向

  • 因为是基于链表,所以插入和删除速度很快,但是查询慢,需要逐个遍历。而且链表需要额外的内存来存储指针域,内存占用多一点。线程不安全

  • Vector(基于数组
    结构和ArrayList基本一致,区别是Vector的方法都实现了同步,所以Vector是线程安全的,性能就会差点。

  • Stack(继承Vector
    继承至Vector,所以它有Vector的所有优缺点,主要是添加了栈相关的操作方法。栈的特点就是后进先出(LIFO)。

  • CopyOnWriteArrayList(ArrayList的线程安全版本)
    CopyOnWrite(写时拷贝)是为了并发而实现的一种懒惰策略。通常我们为了实现并发都是使用锁来实现的,比如使用synchronized关键之等。但是锁会带来性能上的开销。为了提高性能,CopyOnWrite采用的是一种读写分离的思想,其原理如下:

    当需要修改目标数据时,先将目标数据复制一份,并在这份复制的数据上进行修改(此时如果有其他线程来读取数据则正常的从原数据上读取),修改完成后再将数据的引用指向这份复制出来并修改后的数据。这样就在不用锁的条件下实现了对数据的并发读写。

    CopyOnWrite的特点就是:在同一时刻,可以有多个线程进行读操作,但是只能有一个线程进行写操作。所以它相对于ArrayList,是线程安全的。

    缺点:
    1,因为要对数组进行复制,此刻内存中就会存在两份数据,这样会导致写的时候内存占用比较高
    2,不能保证数据的实时一致性,比如在读的时候并不能保证能读取到最新数据,它只能保证数据的最终一致性

问题:ArrayList和LinkedList插入效率哪个高?
  • 少量数据:

    • 首部添加:LinkedList比ArrayList快
    • 尾部添加:LinkedList比ArrayList快

    原因: LinkedList每次增加的时候,会new一个Node对象来存新增加的元素,所以当数据量小的时候,这个时间并不明显,而ArrayList需要频繁扩容,所以LinkedList比ArrayList的效率高。

  • 大量数据(100w-200w)

    • 首部添加:LinkedList比ArrayList快

    原因:因为LinkedList遍历插入位置花费时间很小,而ArrayList需要将原数组所有元素进行一次System.arraycopy,所以扩容和复制数据的时间要大于new对象的时间,所以LinkedList比ArrayList的效率高。

    • 尾部添加:ArrayList比LinkedList快

    原因:当数据量很大的时候,因为ArrayList扩容是当前容量*1.5,大容量扩容一次就能提供很多空间,当ArrayList不需扩容时,而且数组需要复制后移的数据也少了,所以new对象的时间大于扩容的时间,那么就会出现ArrayList’的效率比Linkedlist高。

插入位置越往中间,LinkedList效率越低,因为它遍历获取插入位置是从两头往中间搜,index越往中间遍历越久,因此ArrayList的插入效率可能会比LinkedList高。

插入位置越往后,ArrayList效率越高,因为数组需要复制后移的数据少了,那么System.arraycopy就快了。

以上的结论并不绝对,不同机型效率也有所不同。

Set(不可以存放重复元素)

  • HashSet
    内部是基于HashMap,而HashMap内部又是基于散列表(散列表可以用数组+链表实现)。线程不安全。无序的

  • ArraySet(HashSet的内存优化版本)
    主要是代替HashSet,解决内存占用高的问题。线程不安全。无序的

    ArraySet内部使用了两个数组(一个存储元素的hash值,一个存储元素本身)来代替HashSet中的HashMap。它有几个特点:

    1,初始容量为4(HashSet的初始容量为16);
    2,每次扩容都是原来的1.5倍(HashSet扩容是原来的两倍);
    3,当删除元素时,如果当前使用的容量不到总容量的1/3,则收缩容量,减小内存的占用;
    4,ArraySet会对不再使用的一倍基础容量和二倍基础容量两种数组进行缓存;

    ArraySet的主要索引方法是二分查找,这就导致它的效率要比HashMap低。官方建议是在元素不超过1000条的情况下使用,因为在这种数量级下效率相差不多的,但ArraySet对内存的使用更好

  • TreeSet(HashSet的元素有序版本)
    内部是基于TreeMap,线程不安全。有序的。

  • CopyOnWriteArraySet(HashSet的线程安全版本)
    内部使用的是CopyOnWriteArrayList来实现的,相对于HashSet,是线程安全的。

  • ConcurrentSkipListSet(TreeSet的线程安全版本)
    内部使用的是ConcurrentSkipListMap来实现的,相对于TreeSet,是线程安全的。

Queue(实现了队列(FIFO)相关的功能)

Queue(队列)的特性就是先进先出(FIFO)。所以它主要定义了一些队列的操作方法

  • PriorityQueue
    优先级队列,出队顺序并不是按照先进先出的规则,而是按照优先级,优先级高的最先出队。它的存储结构是一个数组,但是它的逻辑结构却是一颗完全二叉树,具有小顶堆性质。

    PriorityQueue默认使用的是小顶堆,我们也可以使用自定义的Comparator(或Comparable)来实现大顶堆。

  • Deque
    Deque同时具有队列和栈的特性,也就是即可以先进先出,也可以后进先出(LIFO)
    前面已经了解的LinkedList,它也同时实现了Deque接口,而且它借助双向链表可以双向遍历的特性很容易就能实现队列和栈的功能

  • ArrayDeque
    是一个循环队列(可以假想为数组是一个首尾相连的环状),它可以完美的将前面空闲的位置重新利用起来。线程不安全。

Map

  • HashMap
    内部是基于散列表(散列表可以用数组+链表实现)。

    HashMap是通过对key的hash值进行转换来定位每个key在内部数组上的位置。数组的每个元素又都是一个链表,这样如果一个位置有多个键值对的时候就可以依次存放在这个链表上

    HashMap不可以存放重复元素,无序的,线程不安全。

  • LinkedHashMap(HashMap的元素有序化版本)
    主要功能是实现了内部元素的有序化,有两种模式:(在构造方法处指定)
    1,插入顺序
    就是按照插入的时间排序,先插入的数据排在前面,后插入的数据排在后面。
    2,访问顺序
    就是按照最近访问进行排序,访问时间越近的数据排在越靠后的位置,访问时间越远的数据越排在靠前的位置(实现方式就是当元素每次被访问后就将其移动到链表结尾)。LruCache就是基于此原理实现的。

  • TreeMap(HashMap的元素有序化版本,自定义顺序)
    内部是使用红黑树来实现的,有序的,线程不安全。

  • Hashtable(HashMap的线程安全版本)
    相对于HashMap,实现了线程同步,已不建议使用。用ConcurrentHashMap来代替。

  • ConcurrentHashMap(HashMap的线程安全版本)

  • ArrayMap(HashMap的内存优化版本,key被限定为Object)
    是android对HashMap在内存使用上的一个优化版本

    内部是使用两个数组来实现的。一个数组用来保存key的hash值,另一个数组用来保存key和value(每相邻的两个位置表示一个键值对,前面的是key,后面的是value)。类似于ArraySet优化了HashSet。

    内存优化主要体现为:
    1,每次扩容都是原来的1.5倍(HashMap扩容是原来的两倍);
    2,当容量的使用率不到1/3时会收缩容量,释放多余的空间
    3,对两种基础容量数组进行了缓存

  • SparseArray(HashMap的内存优化版本,key被限定为int)
    是android对HashMap在内存使用上的一个优化版本

    和上面的ArrayMap一样,其内部也是使用两个数组来进行存储的

    内存优化主要体现在两个地方:
    1,其内部使用了矩阵压缩算法来优化了稀疏数组;
    2,它的key是int类型,避免了HashMap的自动装箱(将int转换成Integer);
    3,避免了使用一个额外的实体对象来存储键值对。

    因为SparseArray采用了二分查找,所以在大数据下,速度没有HashMap快。官方也是建议在数据不超过1000条的情况下才用来代替HashMap

  • ConcurrentSkipListMap(TreeMap的线程安全版本)
    它使用跳表来对内部的有序链表进行了优化,提高了操作速度

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值