List线性表允许在一个集合中存储重复的元素,而且允许用户指定它们存储的位置,用户可以用下标来访问元素。List接口扩展了Collection接口,增加了一个能够双向线性表的新列表迭代器。AbstractList类提供了List接口的部分实现,AbstractSequentList类扩展了AbstractList类,以提供对链表的支持。
Set接口中,方法listIterator()或listIterator(startIndex)都会返回ListIterator的一个实例。ListIterator接口扩展了Iterator接口,以增加对线性表的双向遍历能力。在Iterator接口中,如果方法next()的返回值非空,该元素将立即被插入到next()方法返回的元素之前;而且,如果方法previous()的返回值非空,该元素将被立即插入到previous()方法返回的元素之后。如果线性表中没有元素,这个新元素即成为线性表中唯一的元素。set(element)方法用于将next()方法或previous()方法返回的最后一个元素替换为指定元素。hasNext()方法用于检测迭代器向前遍历时是否还有元素,而方法hasPrevious()用于检测迭代器向后遍历时是否还有元素。
数组线性表类ArrayList和链表类LinkedList是实现List接口的具体类。ArrayList用数组存储元素,这个数组是动态创建的。如果元素超过了数组的容量,就创建一个更大的新数组,并将当前数组中的所有元素复制到新数组中。LinkedList在一个链表中存储元素。要选用哪一个来要依赖于特定需求。如果需要通过下标随机访问元素,但是除了在末尾处之外,不能在其他位置插入或删除元素,那么ArrayList提供了更高效率的集合。但是,如果应用程序需要在线性表的任意位置上插入或删除元素,就应选择LinkedList类。线性表的大小是可以动态增大或缩小的。然而数组一旦被创建,它的大小就是固定的。如果应用程序不需要在线性表中插入或删除元素,那么数组是效率最高的数据结构。
ArrayList的每个实例都有它的容量,这个容量是指存储线性表的内部数组的大小。它不一定小于所存储的线性表的大小,向ArrayList中添加元素时,其容量会自动增大,但不能自动缩小。可以使用方法trimToSize()将数组容量减小到线性表的大学。LinkedList除了实现List接口外,还提供从线性表两端插入和删除元素的方法。
下面给出简单的ArrayList和LinkedList的程序
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
/**
* @author lxd
* @time 2015-06-11
*/
public class TestArrayAndLinkedList {
public static void main(String[] args) {
List<Integer> arrayList = new ArrayList<Integer>();
arrayList.add(0);
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
arrayList.add(0);
//在指定下标处添加一个元素,其他元素后移
arrayList.add(0, 10);
arrayList.add(3, 40);
System.out.println("A list of integers in the array list:");
System.out.println(arrayList);
//在现有集合上创建一个数组列表
LinkedList<Object> linkedList = new LinkedList<Object>(arrayList);
linkedList.add(1, "aaa");
linkedList.removeLast();
linkedList.addFirst("bbb");
//向前遍历输出
System.out.println("the linked list forward:");
ListIterator<Object> listIterator = linkedList.listIterator();
while (listIterator.hasNext()) {
System.out.print(listIterator.next() + " ");
}
//向后遍历输出
System.out.println("\nthe linked list backward:");
while (listIterator.hasPrevious()) {
System.out.print(listIterator.previous() + " ");
}
}
}
结果:
A list of integers in the array list:
[10, 0, 1, 40, 2, 3, 0]
the linked list forward:
bbb 10 aaa 0 1 40 2 3
the linked list backward:
3 2 40 1 0 aaa 10 bbb
可以看出,0重复存储了两次。ArrayList和LinkedList的操作类似,主要不同体型在内部实现上。同时,为了从泛型类的可变长参数创建线性表,Java提供了静态的asList方法。可以使用下面代码创建一个字符串线性表和整数线性表:
List<String> list1 = Arrays.asList("aaa", "bbb", "ccc");
List<Integer> list2 = Arrays.asList(1, 2, 3, 4);
下面比较一下规则集和线性表的性能:
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.TreeSet;
public class SetListPerformanceTest {
public static void main(String[] args) {
Collection<Integer> set1 = new HashSet<Integer>();
System.out.println("time for Hashset is: " + getTestTime(set1, 500000) + " milliseconds");
Collection<Integer> set2 = new LinkedHashSet<Integer>();
System.out.println("time for LinkedHashSet is: " + getTestTime(set2, 500000) + " milliseconds");
Collection<Integer> set3 = new TreeSet<Integer>();
System.out.println("time for TreeSet is: " + getTestTime(set3, 500000) + " milliseconds");
Collection<Integer> list1 = new ArrayList<Integer>();
System.out.println("time for ArrayList is: " + getTestTime(list1, 60000) + " milliseconds");
Collection<Integer> list2 = new LinkedList<Integer>();
System.out.println("time for LinkedList is: " + getTestTime(list2, 60000) + " milliseconds");
}
public static long getTestTime(Collection<Integer> c, int size) {
long startTime = System.currentTimeMillis();
List<Integer> list = new ArrayList<Integer>();
for (int i=0; i<size; i++) {
list.add(i);
}
//对线性表中的元素进行随机的重新排序
Collections.shuffle(list);
for (int e: list) {
c.add(e);
}
//对线性表中的元素进行随机的重新排序
Collections.shuffle(list);
for (int e: list) {
c.remove(e);
}
long endTime = System.currentTimeMillis();
return endTime - startTime;
}
}
结果如下:
time for Hashset is: 352 milliseconds
time for LinkedHashSet is: 1293 milliseconds
time for TreeSet is: 967 milliseconds
time for ArrayList is: 1601 milliseconds
time for LinkedList is: 4229 milliseconds
通过结果我们可以看出,规则集比线性表更加高效。如果应用程序应用规则集就足够,那就使用规则集。并且,如果程序不需要特别的顺序,就选择散列集(HashSet)
Java集合框架是在Java2中引入的,之前的版本也支持一些数据结构,其中就有向量类Vector和栈类Stack。为了适应Java集合框架,Java2对这些类进行了重新设计,但是为了向后兼容,保留了它们所有的旧形式的方法。
除了包含用于访问和修改向量的同步方法之外,Vector类与ArrayList是一样的。同步方法用于防止两个或多个线程同时访问某个向量时引起数据损坏。对于许多不需要同步的应用程序来说,使用ArrayList比使用Vector效率高。Vector类实现了List接口,它的方法addElement(Object elemnet)方法是同步的,和非同步方法add(Object element)是一样的。方法element()返回一个Enumeration对象(枚举型对象)。Enumeration接口是Java2之前引入的,现在已经被Iterator接口所取代。Java2之前,Vector广泛用于Java程序设计中,因为它可以实现Java可变大小的数组,大多数Swing数据模型使用的都是Vector。
Stack类是作为Vector类的扩展来实现的,方法empty()和isEmpty()的功能是一样的。