List集合代表一个有序、可重复的集合,集合中每个元素都有其对应的顺序索引。List集合默认按元素的添加顺序设置元素的索引,例如第一次添加的元素索引为0,第二次添加的元素索引为1......
与Set集合相比,List增加了根据索引来插入、替换和删除集合元素的方法。
例如:
public class ListTest
{
public static void main(String args[])
{
List books=new ArrayList();
books.add(new String("轻量级Java EE企业应用实战");
books.add(new String("疯狂Java讲义");
books.add(new String("疯狂Android讲义");
System.out.println(books);
//将新字符串对象插入在第二个位置
books.add(1,new String("疯狂Ajax讲义"));
for(int i=0;i<books.size();i++)
{
System.out.println(books.get(i));
}
//删除第三个元素
books.remove(2);
System.out.println(books);
//判断指定元素在List集合中的位置:输出1,表明位于第二位
System.out.println(books.indexOf(new String("疯狂Ajax讲义")));
//将第二个元素替换成新的字符串对象
books.set(1,new String("疯狂Java讲义"));
System.out.println(books);
//将books集合的第二个元素(包括)到第三个元素(不包括)截取成子集合
System.out.println(books.subList(1,2));
}
}
List集合可以根据位置索引来访问集合中的元素,因此List增加了一种新的遍历集合元素的方法:使用普通的for循环来遍历集合元素。
注意:当调用List的set(int index,Object element)方法来改变List集合指定索引处的元素时,指定的索引必须是List集合的有效索引。例如:集合长度是4,就不能指定替换索引为4处的元素——也就是说,set(int index,Object element)方法不会改变list集合的长度。
与Set只提供了iterator()方法不同,List还额外提供了一个listIterator()方法,该方法返回一个ListIterator对象,ListIterator接口继承了Iterator接口,提供了专门操作List的方法。ListIterator接口在Iterator接口基础上增加了如下方法:
boolean hasPrevious():返回该迭代器关联的集合是否还有上一个元素。
Object pervious:返回该迭代器的上一个元素。
void add():在指定位置上插入一个元素。
与Iterator先比,ListIterator增加了向前迭代的功能(Iterator只能向后迭代),而且ListIterator还可通过add方法向List结合中添加元素(List只能删除元素)。
例如:
public class ListIteratorTest
{
public static void main(String args[])
{
String []books={"疯狂Java讲义","轻量级Java EE企业应用实战"};
List bookList=new ArrayList();
for(int i=0;i<books.length;i++)
{
bookList.add(books[i]);
}
ListIterator lit=bookList.listIterator();
while(lit.hasNext())
{
System.out.println(lit.next());
lit.add("---分隔符---");
}
System.out.println("===下面开始反向迭代===");
while(lit.hasPrevious())
{
System.out.println(lit.previous());
}
}
}
ArrayList和Vector类都是基于数组实现的List类,所以ArrayList和Vector类封装了一个动态的、允许再分配的Object[]数组。ArrayList或Vector对象使用initialCapacity参数来设置该数组的长度,当向ArrayList或Vector中添加元素超出了该数组的长度时,它们的initialCapacity会自动增加。
Vector具有很多缺点,通常尽量少用Vector实现类。
ArrayList和Vector的显著区别:ArrayList是线程不安全的,当多个线程访问同一个ArrayList集合时,如果有超过一个线程修改了ArrayList集合,则程序必须手动保证该集合的同步性;但Vector是线程安全的,无须程序保证该集合的同步性。因为Vector是线程安全的,所有Vector的性能比ArrayList的性能要低。实际上,即使需要保证List集合线程安全,也同样不推荐使用Vector实现类。
Vector还提供了Stack子类,它用于模拟“栈”这种数据结构,“栈”通常是指“后进先出”的容器。
例如:
public class VectorTest
{
public static void main(String args[])
{
Stack v=new Stack();
//依次将三个元素“push”入栈
v.push("疯狂Java讲义");
v.push("轻量级Java EE企业应用实战");
v.push("疯狂Android讲义");
System.out.println(v);
//Object peek():访问第一个元素,但并不将其“pop”出栈
System.out.println(v.peek());
//“pop”出栈第一个元素
System.out.println(v.pop());
}
}
Stack继承了Vector,是线程安全的,性能比较差,因此现在的程序中一般较少使用Stack类。如果程序需要使用“栈”这种数据结构,则可以考虑使用LinkedList。
LinkedList也是List的实现类,它是一个基于链表实现的List类,对于顺序访问集合中的元素进行了优化,特别是插入、删除元素时速度非常快。LinkedList既实现了List接口,也实现了Deque接口由于实现了Deque接口,因此可以作为栈来使用。
Arrays:一个操作数组的工具类,该类提供了asList(Object ...a)方法,该方法可以把一个数组或指定个数的对象转换成一个List集合,这个List集合既不是ArrayList实现类的实例,也不是Vector实现类的实例,而是Arrays的内部类ArrayList的实例。
Arrays.ArrayList是一个固定长度的List集合,程序只能遍历访问该集合里的元素,不可增加、删除该集合里的元素。
例如:
public class FixedSizeList
{
public static void main(String args[])
{
List fixedList=Arrays.asList("疯狂Java讲义","轻量级Java EE企业应用实战");
//获取fixedList的实现类,将输出Arrays$ArrayList
System.out.println(fixedList.getClass());
//遍历fixedList的集合元素
for(int i=0;i<fixedList.size();i++)
{
System.out.println(fixedList.get(i));
}
//试图增加、删除元素都会引发UnsupportedOperationException异常
fixedList.add("疯狂Android讲义");
fixedList.remove("疯狂Java讲义");
}
}
Queue用于模拟队列这种数据结构,,队列通常是指“先进先出”的容器。新元素插入(offer)到队列的尾部,访问元素(poll)操作操作会返回队列头部的元素。通常,队列不允许随机访问队列中的元素。
Queue接口有一个PriorityQueue实现类。除此之外,Queue还有一个Deque接口,Deque代表一个“双端队列”,双端队列可以同时从两端来添加、删除元素,因此Deque的实现类既可当成队列使用,也可当成栈使用。Java为Deque提供了ArrayDeque和LinkedList两个实现类。
PriorityQueue是一个比较标准的队列实现类,其保存队列元素的顺序并不是按加入队列的顺序,而是按队列元素的大小进行重新排序。因此当调用peek()方法或者poll()方法取出队列中的元素时,并不是取出最先进入队列的元素,而是取出队列中最小的元素。从这个意义上看,PriorityQueue已经违反了队列的最基本规则:先进先出(FIFO)。
例如:
public class PriorityQueueTest
{
public static void main(String args[])
{
PriorityQueue pq=new PriorityQueue();
//下面代码依次向pq中加入四个元素
pq.offer(6);
pq.offer(-3);
pq.offer(9);
pq.offer(0);
//输出pq队列,并不是按元素的加入顺序排列
System.out.println(pq);
//访问队列的第一个元素,其实就是队列中最小的元素:-3
System.out.println(pq.poll());
}
}
Deque接口提供了一个典型的实现类:ArrayDeque,它是一个基于数组实现的双端队列。
下面程序示范了把ArrayDeque当成“栈”来使用。
public class ArrayDequeTest
{
public static void main(String args[])
{
ArrayDeque stack=new ArrayDeque();
//依次将三个元素push入“栈”
stack.push("疯狂Java讲义");
stack.push("轻量级Java EE企业应用实战");
stack.push("疯狂Android讲义");
System.out.println(stack);
//访问第一个元素,但并不将其pop出“栈”,输出:疯狂Android讲义
System.out.println(stack.peek());
//pop出第一个元素,输出“疯狂Android讲义”
System.out.println(stack.pop());
}
}
LinkedList类是List接口的实现类,它是一个List集合,可以根据索引来随机访问集合中的元素。除此之外,LinkedList还实现了Deque接口,因此它可以被当成双端队列来使用,自然也可以当成“栈”来使用了。
例如:
public class LinkedListTest
{
public static void main(String args[])
{
LinkedList books=new LinkedList();
//将字符串元素加入队列的尾部
books.offer("疯狂Java讲义");
//将字符串元素添加入栈的顶部
books.push("轻量级Java EE企业应用实战");
//将字符串元素添加到队列的头部(相当于栈的顶部)
books.offerFirst("疯狂Android讲义");
for(int i=0;i<books.size();i++)
{
System.out.println(books.get(i));
}
//访问但不删除栈顶的元素
System.out.println(books.peekFirst());
//访问但不删除队列的最后一个元素
System.out.prinln(books.peekLast());
//将栈顶的元素弹出“栈”
System.out.prinln(books.pop());
//访问并删除队列的最后一个元素
System.out.println(books.pollLast());
}
}
ArrayList、ArrayDeque内部以数组的形式来来保存集合中的元素,因此随机访问集合元素时有较好的性能;而LinkedList内部以链表的形式来保存集合中的元素,因此随机访问集合元素时性能较差,但在插入、删除元素时性能非常出色。需要指出的是Vector也是以数组形式来存储集合元素的,但因为实现了同步功能,所以各方面性能都有所下降。