注:
多线程情况下,各个集合可以考虑使用java.util.concurrent(JDK1.5)包下的集合类。
Java最常用的集合类,顶层接口Collection和Map两个接口
(1)Collection接口:(包括List,Set,Queue三大子接口)
1---List接口(对象按照索引位置排序,可以有重复对象,允许按照对象在集合中的索引位置检索对象)
1, ArrayList 线程不同步,数组结构,查询快,增删慢
2, LinkedList线程不同步,链表结构,增删快,查询慢
3, Vector线程同步,数组结构,都慢,过时
--Stack
4, CopyOnWriteArrayList线程同步,数组结构,写时同步拷贝一个副本操作,读写分离,不适合实时操作,不适合大容量
2---Set接口(不允许有重复元素,可用于去重操作,对象不按特定方式排序,并且没有重复对象。但它的有些实现类能对集合中的对象按特定方式排序,例如TreeSet类,它可以按照默认排序,也可以通过实现java.util.Comparator接口来自定义排序方式)
1, HashSet线程不同步,哈希表结构(其数据是一个HashMap),无序,可以有一个null
--LinkedHashSet有序,链表维护次序,迭代时比HashSet快,插入时稍慢
2, SortedSet (接口)
--TreeSet线程不同步,二叉树结构,有序
3, ArraySet(Android)数组结构,节省内存(数据在千级以内时)
3---Queue接口(暂不研究)
(2)Map接口(每一个元素包含一个键对象和值对象,它们成对出现。键对象不能重复,值对象可以重复。)
1, HashMap线程不同步,哈希表结构,key和value都允许是null(key允许一个null)
-- LinkedHashMap 保留插入的顺序
2, HashTable过时?线程同步,哈希表结构,key和value都不允许是null
3, SortedMap(接口)线程不同步,有序,基于红黑树结构
--TreeMap
4, WeakHashMap键是弱键
5, ConcurrentMap(接口)
-- ConcurrentHashMap优化的线程安全(分段锁,默认16段)
6, ArrayMap(Android)节省内存,二分查找法,比HashMap慢
Android中Map相关(未实现Map接口):
(1) SparseArray数据小于1000时,替代HashMap,节省内存
(2) SparseIntArray,SparseLongArray,SparseBooleanArray只存储对应类型值
ArrayMap,SparseArray,HashMap比较:
1, ArrayMap与SparseArray比较:
(1) SparseArray的key只能是int类型(因为其原理是二分检索法),ArrayMap的key可以是其它类型
2, 如果key的类型已经确定为int类型,那么使用SparseArray,因为它避免了自动装箱的过程,如果key为long类型,它还提供了一个LongSparseArray来确保key为long类型时的使用。
3, HashMap和SparseArray比较:
当存储大量数据(起码上千个)的时候,优先选择HashMap。如果只有几百个,用哪个区别不大。如果数量不多,优先选择SparseArray。
(1) SpareseArray 也是通过键值对存储数据,只是key为整形int , 类似于key = Interger 的HashMap,但是SpareseArray 的key 为 int 非 Interger ,更省空间。
(2) SpareArray 意为稀松数组,其结构类似于数组结构,依次排开;HashMap是散列列表,根据hash值来存储;因此SpareArray 会比 HashMap节省很多空间。
(3) 从查找速度和 插入效率来看,如果是正序插入( 0 ->size插入),SpareArray 的插入效率会高于 HashMap。
(4) 如果是逆序插入(size -> 0)的顺序插入,则SpareArray 的插入效率表现是最差的,会低于HashMap。
(5) SpareArray 在逆序插入效率很低,是因为每次插入SpareArray 都会采用二分查找来定位。
(6) 从查找速度来来考虑,HashMap的查找速度会高于SparseArray。于是,通过以上分析,SpareArray 相对于 HashMap的最大优势在内存空间。因此谷歌推荐使用 SpareArray 代替 HashMap 。
一,List接口
List选用标准:
1,单线程,不需要考虑线程安全时
(1) 在查询(get)、遍历(iterator)、修改(set)使用的比较多的情况下,用ArrayList
(2) 在增加(add)、删除(remove)使用比较多的情况下,用LinkedList
2,多线程,需要考虑线程安全时,有以下几种方法:
(1) 用ArrayList或LinkedList,并用Synchoronized加锁
(2) 用ArrayList或LinkedList,并用Collections.SynchronizedList(list)静态方法加锁(遍历时仍需手动加锁)
(3) 用Vector(过时,不推荐用,因为其所有操作都是加锁的,严重影响效率),(遍历时仍需手动加锁)。
(4) 在需要使用栈结构的情况下,使用Deque,Stack废弃就行了。
(5) 用CopyOnWriteArrayList(原理是发生修改操作时做copy,新老版本分离,保证读的高性能,适用于以读为主,读操作远远大于写操作的场景中使用,比如缓存)。
CopyOnWriteArrayList特点:
1, 不能用于实时读的场景。因为调用一个set操作后,读取到数据可能还是旧数组的,由于其读写分离,读和写分开,只能保证最终结果一致性。
2, 不适合大量内容的场景。因为写操作的时候,需要拷贝数组,会消耗内存,如果原数组的内容比较多的情况下,可能导致gc,性能下降严重。
线程同步方法Collections.SynchronizedList()与Vector的区别
1, SynchronizedList有很好的扩展和兼容功能。他可以将所有的List的子类转成线程安全的类。
2, 使用SynchronizedList的时候,进行遍历时要手动进行同步处理。(遍历时Vector也需要手动加锁)
3, SynchronizedList可以指定锁定的对象。
4, SynchronizedList方法比Vector慢
ArrayList与Vector主要区别为以下几点:
(1):Vector是线程安全的,源码中方法(包括get)都是synchronized,而ArrayList不是。导致Vector效率无法和ArrayList相比;
(2):ArrayList和Vector都采用线性连续存储空间,当存储空间不足的时候,ArrayList默认增加为原来的50%,Vector默认增加为原来的一倍;
(3):Vector可以设置capacityIncrement,即每次扩容时容量增长值,该值默认值0表示每次默认增长为原来的长度值。而ArrayList不可以设置。
1、ArrayList
非线程安全
基于对象数组
get(int index)不需要遍历数组,速度快;
iterator()方法中调用了get(int index),所以速度也快
set(int index, E e)不需要遍历数组,速度快
add方法需要考虑扩容与数组复制问题,速度慢
remove(Object o)需要遍历数组,并复制数组元素,速度慢
remove(int index)不需要遍历数组,需要复制数组元素,但不常用
contain(E)需要遍历数组
是最常用的List实现类,内部是通过数组实现的,它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要讲已经有数组的数据复制到新的存储空间中。当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除。
2、LinkedList
非线程安全
基于环形双向链表
get(int index)需要遍历链表,速度慢;
iterator()方法中调用了get(int index),所以速度也慢
set(int index, E e)方法中调用了get(intindex),所以速度也慢
add方法不需要考虑扩容与数组复制问题,只需创建新对象,再将新对象的前后节点的指针指向重新分配一下就好,速度快
remove(Object o)需要遍历链表,但不需要复制元素,只需将所要删除的对象的前后节点的指针指向重新分配一下以及将所要删除的对象的三个属性置空即可,速度快
remove(int index)需要遍历链表,但不需要复制元素,只需将所要删除的对象的前后节点的指针指向重新分配一下以及将所要删除的对象的三个属性置空即可,但不常用
contain(E)需要遍历链表
是用链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较慢。另外,他还提供了List接口中没有定义的方法,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。
3、Vector(线程安全的ArrayList)
线程安全
扩容机制与ArrayList不同
与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一个线程能够读写Vector,避免多线程同时读写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问ArrayList慢。
4、Stack(继承于Vector)
线程安全
效率低下,可采用双端队列Deque或LinkedList来实现,Deque用的较多
线程安全 | 底层实现 | 查询速度 | 增删速度 | 特点 | ||
List集合 | ArrayList | 否 | 数组 | 很快 | 较慢 | 查询很快,但增删较慢 |
LinkedList | 否 | 链表 | 较慢 | 较快 | 查询较慢,增删较快 | |
Vector | 是 | 数组 | 慢 | 慢 | 无论查询还是增删都很慢,被ArrayList替代了 | |
Stack(继承于Vector) | 是 | 数组 |
相关基础:
数组
数组存储区间是连续的,占用内存严重,故空间复杂的很大。但数组的二分查找时间复杂度小,为O(1);数组的特点是:寻址容易,插入和删除困难;
链表
链表存储区间离散,占用内存比较宽松,故空间复杂度很小,但时间复杂度很大,达O(N)。链表的特点是:寻址困难,插入和删除容易。
哈希表
那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?答案是肯定的,这就是我们要提起的哈希表。哈希表((Hash table)既满足了数据的查找方便,同时不占用太多的内容空间,使用也十分方便。
二,Set接口
Set接口,主要有HashSet 和TreeSet , LinkedHashSet
Set不允许包含相同的元素,如果试图把两个相同元素加入同一个集合中,add方法返回false。
Set判断两个对象相同不是使用==运算符,而是根据equals方法。也就是说,只要两个对象用equals方法比较返回true,Set就不会接受这两个对象。
HashSet
HashSet有以下特点
不能保证元素的排列顺序,顺序有可能发生变化Ø
不是同步的Ø
集合元素可以是null,但只能放入一个nullØ
当向HashSet结合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据 hashCode值来决定该对象在HashSet中存储位置。
简单的说,HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode()方法返回值相等
注意,如果要把一个对象放入HashSet中,重写该对象对应类的equals方法,也应该重写其hashCode()方法。其规则是如果两个对象通过equals方法比较返回true时,其hashCode也应该相同。另外,对象中用作equals比较标准的属性,都应该用来计算 hashCode的值。
LinkedHashSet(继承HashSet)
LinkedHashSet集合同样是根据元素的hashCode值来决定元素的存储位置,但是它同时使用链表维护元素的次序。这样使得元素看起来像是以插入顺序保存的,也就是说,当遍历该集合时候,LinkedHashSet将会以元素的添加顺序访问集合的元素。
LinkedHashSet在迭代访问Set中的全部元素时,性能比HashSet好,但是插入时性能稍微逊色于HashSet。
TreeSet类
TreeSet是SortedSet接口的唯一实现类,TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序和定制排序,其中自然排序为默认的排序方式。向TreeSet中加入的应该是同一个类的对象。
TreeSet判断两个对象不相等的方式是两个对象通过equals方法返回false,或者通过CompareTo方法比较没有返回0
自然排序
自然排序使用要排序元素的CompareTo(Object obj)方法来比较元素之间大小关系,然后将元素按照升序排列。
Java提供了一个Comparable接口,该接口里定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现了该接口的对象就可以比较大小。
obj1.compareTo(obj2)方法如果返回0,则说明被比较的两个对象相等,如果返回一个正数,则表明obj1大于obj2,如果是负数,则表明obj1小于obj2。
如果我们将两个对象的equals方法总是返回true,则这两个对象的compareTo方法返回应该返回0
定制排序
自然排序是根据集合元素的大小,以升序排列,如果要定制排序,应该使用Comparator接口,实现 int compare(T o1,T o2)方法
三,Map接口
优化:
推荐的数据结构:
· ArrayMap<K,V> 替代 HashMap<K,V>
· ArraySet<K,V> 替代 HashSet<K,V>
· SparseArray<V> 替代 HashMap<Integer,V>
· SparseBooleanArray 替代 HashMap<Integer,Boolean>
· SparseIntArray 替代 HashMap<Integer,Integer>
· SparseLongArray 替代 HashMap<Integer,Long>
· LongSparseArray<V> 替代 HashMap<Long,V>