一:基本概念
Java的所有集合都放在java.util包下,集合跟数组不同之处在于前者只能保存对象(对象的引用),而后者不仅能保存对象,还能保存基本数据类型。
Java的集合类主要有两个接口派生而出:Collection和Map,本文主要介绍Collection,其继承图如下:
Java中Collection接口继承图Collection接口里常用的方法有:
boolean add(Object o):添加元素,如果添加成功则返回true。
boolean addAll(Collection c):把集合c全部添加到指定集合,如果添加成功则返回true。
void clear():清除集合元素,并将集合长度变成0。
boolean contains(Object o):是否包含元素。
boolean containsAll(Collection c):是否包含集合c的全部元素。
boolean isEmpty():是否为空。
Iterator iterator():返回一个Iterator对象,用于便利集合元素。
boolean remove(Object o):删除元素o,当集合有多个元素符合条件时,只删除第一个对象。
boolean removeAll(Collection c):从集合中删除集合c,如果删除一个或一个以上的元素则返回true。
boolean retainAll(Collection c):从集合中删除c中不包含的元素,相当于把调用该方法的集合变成该集合与c的交集。
int size():返回元素个数。
Object[] toArray():把集合转换成一个数组,集合元素变成数组元素。
二:Set集合
Set集合不允许包含相同的元素,且Set的三个实现类都是线程不安全的。
HashSet和TreeSet是Set的两个典型实现,HashSet的性能总是比TreeSet要好,因为TreeSet需要额外的红黑树算法来维护集合元素的次序。只有当需要一个保持次序的Set时,才应该使用TreeSet,否则都应该使用HashSet。HashSet的子类LinkedHashSet由于需要维护链表,普通操作比如插入、删除会比HashSet稍微慢一些,但是遍历LinkedHashSet会更快。
EnumSet是所有集合类中性能最好的,但它只能保存同一个枚举类的枚举值作为集合元素。
1,HashSet
HashSet不能保证元素的顺序,非同步,且集合元素可以是null。
HashSet存储对象时,会调用对象的hashCode方法,使用获得的hashCode值来决定存储的位置。注意,HashSet判断两个对象相等的标准是hashCode值相等以及equals()方法比较之后相等。如果两个元素返回的hashCode值相等,但是equals调用的结果不相等,HashSet就会把他们放在同一个位置存储(采用链表的形式管理),造成性能下降。
HashSet有一个LinkedHashSet子类,用hashCode决定存储位置,同时采用链表维护元素次序。当遍历LinkedHashSet时,元素的顺序跟存储时的顺序一致。
2,TreeSet
TreeSet是SortedSet接口的实现类,TreeSet可以保证集合元素处于排序状态。与HashSet相比 ,TreeSet提供以下几个额外的方法:
Comparator comparator():返回排序使用的comparator,如果采用默认排序则返回null。
Object first():返回集合中第一个元素。
Object last():返回集合中最后一个元素。
Object lower(Object e):返回小于指定元素的最大元素,且参考元素不需要是TreeSet里的元素。
Object higher(Object e):返回大于指定元素的最小元素。
SortedSet subSet(Object fromElement, Object toElement):返回一个范围内的子集。
3,EnumSet
EnumSet是一个专门为枚举类设计的集合类,EnumSet中的所有元素必须是指定枚举类型的枚举值,EnumSet是有序的,以枚举值在 Enum类中的定义顺序来决定集合元素的顺序。
EnumSet不允许加入null元素,当试图添加null值时会抛出NullPointerException异常。
EnumSet没有暴露任何构造器来创建实例,程序应当通过它提供的类方法来创建EnumSet对象:
EnumSet allOf(Class elementType):创建一个包含指定枚举类里的所有枚举值的集合。
EnumSet complementOf(EnumSet e):创建一个新集合包含原EnumSet不包含的、枚举类剩下的枚举值。
EnumSet copyOf(Collection c):使用一个普通集合来创建枚举集合。
EnumSet copyOf(EnumSet e):创建一个与e同元素类型、同集合元素的集合。
EnumSet noneOf(Class elementType):创建一个枚举元素类型为指定枚举类的空EnumSet。
EnumSet of(E first...E last):创建一个包含一个或多个枚举值的枚举集合,传入的多个枚举值必须为同一类型。
EnumSet range(E from,E to):创建一个包含从from到to范围内所有枚举值的EnumSet集合。
三,List集合
List集合代表元素有序、可重复的集合,集合中每个元素都有其对应的顺序索引。
在Collection的基础上,List增加了一些根据索引来操作集合元素的方法:
void add(int index,Object element):将元素element插入到List集合的index处。
boolean addAll(int index,Collection c):将集合c的所有集合元素都插入到List集合的index处。
Object get(int index):返回索引值index处的元素。
int indexOf(Object o):返回对象o在LIst中第一次出现的index值。
int lastIndexOf(Object o):返回对象o在List中最后一次出现的index值。
Object remove(int index):删除并返回index索引处的元素。
Object set(int index,Object element):将index处的元素替换成element对象,并返回被替换的Object。
List subList(int fromIndex,int toIndex):返回从索引fromIndex(包含)到toIndex(不包含)处所有集合元素组成的子集合。
void replaceAll(UnaryOperator operator):根据operator的计算规则重新设置List集合的所有元素。
void sort(Comparator c):根据Comparator参数对List集合重新排序。
1,ArrayList 和Vector实现类
ArrayList和Vector是List的典型实现,都基于数组,封装了一个动态的、允许再分配的Object[]数组,ArrayList和Vector使用initialCapacity参数来设置该数组的长度,当添加元素的时候,该参数就会自动增加。一般来说,程序员无需关心initialCapacity,但如果向List中添加大量元素时可以使用ensureCapacity(int minCapacity)方法一次性的增加initialCapacity,减少重分配次数从而提高性能。如果创建新的空List集合时不指定initialCapacity参数,则Object[]默认长度为10。
此外,ArrayList和Vector还提供以下两个方法重新分配Object[]数组:
void ensureCapacity(int minCapacity):将集合的Obj[]数组长度增加大于或者等于minCapacity值。
void trimToSize():调整集合的Object[]数组长度为当前元素的个数,调用该方法可减少集合对象占用的空间。
Vector和ArrayList几乎完全相同,Vector是一个古老的集合实现类,因此有很多缺陷。两者最显著的区别在于,Vector相比ArrayList,是线程安全的。Vector有一个Stack子类,用以模拟“栈”这种数据结构。
2,LinkedList
LinkedList既是List的实现类,从继承图中还可以看出它实现了Deque接口,可以当成双端队列来使用,是一个功能非常强大的集合类。
四,Queue集合
Queue集合用于模拟队列这种数据结构,特点为“先进先出”(FIFO),新元素插入(offer)到队列的尾部,访问元素(poll)操作会返回队列头部的元素,通常队列不允许随机访问队列中的元素。Queue接口有一个PriorityQueue实现类和一个Deque接口。
Queue接口定义以下几个方法:
void add(Object o):将指定元素加入队列尾部。
boolean offer(Object o):将指定元素添加到队列的尾部,当使用有容量限制的队列时,该方法比add要好。
Object element():获取头部元素但是不删除它。
Object peek():获取队列头部元素但是不删除它,如果队列为空则返回null。
Object poll():获取队列元素并删除该元素,如果队列为空则返回null。
Object remove():获取队列头部元素并删除它。
1,PriorityQueue
PriorityQueue并不是按照添加元素的顺序来排列元素,而是按照队列元素的大小进行由小到大排序,所以PriorityQueue不允许插入null元素。跟TreeSet类似,PriorityQueue的元素有两种排序方式:自然排序和定制排序。
2,Deque接口和ArrayDeque实现类
Deque接口代表一个双端队列,同时可以作为栈来使用,此接口定义了一些允许从队列两端来操作元素的方法:
void addFirst(Object o):将指定元素插入到该双端队列的开头。
void addLast(Object o):将指定元素插入到该双端队列的末尾。
Iterator descendingIterator():返回该双端队列对应的迭代器,该迭代器以逆向顺序来迭代队列中的元素。
Object getFirst():获取但不删除双端队列的第一个元素。
Object getLast():获取但不删除双端队列的最后一个元素。
boolean offerFirst(Object o):将指定元素插入到该双端队列的开头。
boolean offerLast(Object o):将指定元素插入到该双端队列的结尾。
Object peekFirst()获取但不删除该双端队列的第一个元素,如果队列为空则返回null。
Object peekLast()获取但不删除该双端队列的最后一个元素,如果队列为空则返回null。
Object pollFirst()获取并删除该双端队列的第一个元素,如果队列为空则返回null。
Object pollLast()获取并删除该双端队列的最后一个元素,如果队列为空则返回null。
Object pop()(栈方法):pop出该双端队列所表示的栈的栈顶元素,相当于removeFirst()。
void push(Object o)(栈方法):将一个元素push进该双端队列所表示的栈的栈顶,相当于addFirst(o)。
Object removeFirst():获取并删除该双端队列的第一个元素。
Object removeFirstOccurrence(Object o):获取并删除该双端队列第一次出现的元素o;
Object removeLast():获取并删除该双端队列的最后一个元素。
Object removeLastOccurrence(Object o):获取并删除该双端队列最后一次出现的元素o;
Java提供的List就是线性表接口,而ArrayList和LinkedList又是线性表的两种典型实现:基于数组的线性表和基于链的线性表,Queue代表队列,Deque代表双端队列。一般来说,由于数组一块连续区域来保存所有的数组元素,所以数组在随机访问时性能最好——所有的内部以数组为底层实现的集合在随机访问时性能都比较好,而内部以链表作为底层实现的集合在执行插入删除操作时有更好的性能——但总体来说 ,ArrayList的性能要比LinkedList要好。
关于List集合有如下建议:
# 如果需要遍历List集合元素,对于ArrayList和Vector,应该使用随机访问方法(get),对于LinkedList,应该使用迭代器(Iterator)。
# 如果需要经常执行插入、删除来改变包含大量数据的List集合的大小,可考虑使用LinkedList集合,使用ArrayList和Vector由于需要经常重新分配数组大小,效果可能比较差。
# 如果有多个线程需要同时访问List集合中的元素,开发者可考虑使用Collections将集合包装成线程安全的集合。
网友评论