一、体系概述
Java集合类是一个很有用的工具类,可以用于存储数量不等的多个对象,并可以实现常用数据结构,如栈、队列等。除此之外,Java集合还可用于保存具有映射关系的关联数组。Java集合大致可分为Set、List和Map三种体系,其中Set代表无序、不可重复的集合;List代表有序、可重复的集合;而Map则代表具有映射关系的的集合。从Java5以后,Java又增加了Queue体系集合,代表一种队列集合实现。
Java集合就像一种容器,可以存储多个对象。Java5之前,Java集合会丢失容器中所有对象的数据类型,把所有对象都当成Object类型处理;从Java5引进泛型以后,Java集合可以记住容器中对象的类型,从而可以简化代码编写。
集合与数组的不同:
1、 数组虽可以存储对象,但长度是固定的;集合长度可变。
2、 数组可以存储基本数据类型;而集合只能存储对象。
3、 数组只能存储同一类型对象,而集合可以存储不同类型对象。
二、Collection和Iterator接口
1、Collection接口
Collection接口是List、Set、和Queue接口的父接口。Collection接口里面定义了如下操作结合元素的方法:
——boolean add(Object o):向集合里添加一个元素。添加成功则返回true。
——boolean addAll(Collection c):将c里面所有元素添加到指定集合。添加成功返回true。
——void clear():清除集合里面的所有元素。
——boolean contains(Object o):返回集合是否包含指定元素o。
——boolean containsAll(Collection c):返回集合是否包含集合成c里面的所有元素。
——boolean isEmpty():返回集合是否为空。
——Iterator iterator():返回一个Iterator对象。
——boolean remove(Object o):删除指定元素o,当集合中包含一个或多个元素o时,o将被删除,并返回true。
——boolean removeAll(Collection c):从集合里删除集合c所包含的所有元素,如果删除了一个或一个以上元素,返回true。
——boolean retainAll(Collection c):从集合中删除集合c不包含的所有元素,如果改变了原集合,则返回true。
——int size():返回集合元素个数。
——Object[] toArray():将集合转成数组。
2、Iterator接口
该接口主要用于遍历集合中的元素,也被称为迭代器。该接口定义了如下三个方法:
——boolean hasNext():如果被迭代的集合元素还没有被遍历,则返回true。
——Object next():返回集合里的限一个元素。
——void remove():删除集合里上一次next方法返回的元素。
三、List集合
1、List接口和ListIterator接口
List集合相对于Coolection接口增加了一些根据索引来操作集合元素的方法:
——void add(int index, Object element):将元素element插入到集合的index处。
——boolean addAll(int index, Collection c):将集合c中的所有元素添加到Index处。
——Object get(int index):返回集合index索引处的元素。
——int indexOf(Object o):返回o对象第一次出现在集合中的位置。
——Object remove(int index):删除并返回index处的元素。
——Object set(int index , Object element):将index处的对象替换成element,并返回新元素。
——List subList(int fromIndex,int toIndex):返回从索引fromIndex(包含)到toIndex(不包含)处所有集合元素组成的子集合。
ListIterator接口:
List提供了一个listIterator()方法,返回一个ListIterator对象,ListIterator接口继承了Iterator接口,它额外提供了如下方法:
——boolean hasPrevious():返回集合是否还有上一个元素。
——Object previous():返回上一个元素。
——void add():在指定位置插入一个新元素。
2、ArrayList和Vector实现类
List接口有三个典型实现类:ArrayList、Vector、LinkedList。
其中,ArrayList和Vector底层数据结构都是数组,因而查询速度快,增删慢;而LinkedList底层是链表数据结构,它的查询速度慢但增删速度较快。另对于ArrayList和Vector而言也有不同,前者线程不安全,但效率高,后者线程安全但效率低,现在已基本被ArrayList取代。
LinkedList既实现了List接口,也实现了Deque接口,由于后者,因此可以作为栈来使用。
3、Array.ArrayList内部类
操作数组的工具类Arrays提供了一个asList(Object… a)方法,该方法可以把一个数组或指定数量的对象转成一个List集合,这个List集合既不是ArrayList实现类的实例,也不是Vector的实例,而是Arrays的内部类ArrayList的实例。
Arrays.ArrayList是一个固定长度的List集合,程序只能遍历访问该集合里的元素,不可增加、删除操作。如果试图进行增删操作,则会引发UnsupportedOperationException异常。
四、Queue集合
Queue用于模拟队列这种数据结构。新元素插入(offer)到队列尾部,访问元素(poll)操作会返回队列头部元素。通常,队列不允许随机访问队列中的元素。Queue接口定义了如下几个方法:
——void add(Object e):将指定元素添加到队列尾部。
——Object element():获取队列头部的元素,但不删除。
——boolean offer(Object o):将指定元素添加到队列尾部。当使用有容量限制的队列时,此种方法较add方法更好。
——Object peek():获取队列头部的元素,但不删除。如果队列空,返回null。
——Object poll():获取队列头部的元素,并删除。如果队列空,返回null。
——Object remove():获取队列头部的元素,并删除。
1、PriorityQueue实现类
PriorityQueue实现类保存队列的顺序并不是按加入队列的顺序,而是按队列元素大小进行重新排序。因此,当调用peek()方法或poll()方法取出元素时,并不是取出最先进入队列的元素,而是取出最小的元素。
Tips:
如果直接输出整个队列,会发现并不是按从小到大的顺序排列,那是因为受到toString方法的返回值影响。
PriorityQueue不允许插入null元素,它还需要对队列元素进行排序,有两种方式:
——自然排序:采用此种方式排序的集合必须实现了Comparable接口,而且应该是同一个类的多个实例,否则可能导致ClassCastException异常。
——定制排序:创建队列时,传入一个Comparator对象,该对象负责对队列中元素进行排序。此时不要求队列实现Comparable接口。
2、Deque接口与ArrayDeque实现类
Deque接口是Queue的字接口,它代表一个双端队列,主要方法有如下一些:
——void addFirst(Object e):将元素插入到双端队列开头。
——void addLast(Object e):将元素插入到双端队列结尾。
——Iterator descendingIterator():返回一个迭代器,该迭代器将以逆向顺序来迭代队列元素。
——Object getFirst():获取但不删除第一个元素。
——Object getLast():获取但不删除最后一个元素。
——boolean offerFirst(Object e):将元素插入到双端队列开头。
——boolean offerLast(Object e):将元素插入到双端队列结尾。
……
Deque接口提供了一个典型的实现类:ArrayDeque,它是一个基于数组实现的双端队列,创建Deque时可以指定一个numElements参数,该参数用于指定Object[] 数组的长度;如果不指定numElements参数,Deque底层数组长度为16。
3、LinkedList实现类
LinkedList是List集合的实现类,意味着它可以根据索引来随机访问集合中的元素。除此之外,它还实现了Deque接口,意味着它可以被当作双端队列来使用,自然也可以被当作栈来使用。
例:
import java.util.LinkedList;
public class LinkedListDemo {
public static void main (String[] args)
{
LinkedList<String> books = new LinkedList<String>();
//将字符串添加到队列尾部
books.offer("三国演义");
//将字符串添加到栈的顶部
books.push("水浒传");
//将字符串添加到队列头部(相当于栈的顶部)
books.offerFirst("红楼梦");
for(int i = 0; i < books.size(); i++)
{
System.out.println(books.get(i));
}
//访问但不删除栈顶元素
System.out.println(books.peekFirst());
//访问但不删除队列的最后一个元素
System.out.println(books.peekLast());
//将栈顶的元素弹出栈
System.out.println(books.pop());
//访问并删除队列的最后一个元素
System.out.println(books.pollLast());
//此时队列中只剩下一个元素“水浒传”
System.out.println(books);
}
}
4、各种线性表的性能分析
Java提供的List就是一个线性表接口,而ArrayList、LinkedList又是线性表的两种典型实现类:基于数组的线性表和基于链的线性表。
下面对各种实现类性能进行分析:
实现机制
随机访问排名
迭代操作排名
插入操作排名
删除操作排名
数组
连续内存区保存
1
不支持
不支持
不支持
ArrayList/ArrayDeque
以数组保存
2
2
2
2
Vector
以数组保存
3
3
3
3
LinkedList
以链表保存
4
1
1
1
Tips:
如果运行程序时,JVM默认的存储空间不足以运行,可以采用如下命令改变JVM内存大小:
java –Xms128m –Xmx512m java源文件名.java
//-Xms是设置JVM的堆内存初始大小
//-Xmx 是设置JVM的堆内存最大大小
Tips:
关于使用List集合有如下建议:
——如需遍历List集合,对于ArrayList、Vector集合,应该采用随机访问方法get()来遍历,这样性能更好;对于LinkedList集合,则应该采用迭代器Iterator来遍历集合元素。
——如果需要经常执行插入、删除操作来改变List集合大小,则应该使用LinkedList集合。
——如果有多个线程需要同时访问List集合中的元素,可考虑使用Collections将集合包装成线程安全的集合。