List 接口继承自 Collection,在上一章 Java 容器 中已经介绍过,List 接口在 Collection 的基础上添加了大量的方法,使得可以在 List 的中间插入和移除元素。List 有两种类型:
- ArrayList,它随机访问元素速度较快,但是在 List 的中间插入和移除元素时较慢;
- LinkedList,它可以以较低的代价在 List 中间进行插入和删除操作,提供了优化的顺序访问,LinkedList 在随机访问方面相对较慢,总体说来,LinkedList 包含的特性要多于 ArrayList。
ArrayList
ArrayList 实现了 List 接口,可以当做一个可扩充尺寸的数组,使用比较简单,通过 add() 方法添加元素,然后通过 get() 方法获取元素:
public class ArrayListTest {
public static void main(String[] args) {
//创建 list 对象(向上转型)
List<Pet> list = new ArrayList<>();
//添加元素
list.add(new Cat());
list.add(new Dog());
//遍历集合并打印
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i).getName());
}
}
}
class Cat extends Pet {
public String getName() {
return "Tom";
}
}
class Dog extends Pet {
public String getName() {
return "Sam";
}
}
class Pet {
public String getName() {
return "Pet";
}
}
上面的例子通过 ArrayList 向上转型来创建一个 List 对象,其中尖括号括起来的就是类型参数,它指定了这个 list 容器可以保存的元素类型,通过使用泛型,就可以在编译器防止将错误类型的对象放置在容器中。
当指定了某个类型作为泛型参数时,不仅可以将该类型的对象放置在容器中,也可以将该类型的子类型对象放置在容器中。比如上例中声明的是 Pet 类型,却可以放进 Cat 和 Dog 对象,因为 Cat 和 Dog 继承了 Pet 类。如果没有声明泛型,那么就默认类型为 Object,可以放任何类型的对象。
我们接着来看看 ArrayList 的其它操作:
public static void main(String[] args) {
List<Integer> list1 = new ArrayList<>();
//集合的数值范围 100 ~ 109
for(int i = 100; i < 110; i++){
list1.add(i);
}
System.out.println("list1: " + list1);
System.out.println("100 的下标:" + list1.indexOf(101));
System.out.println("删掉第 0 个元素: " + list1.remove(0));
//截取前五个元素作为一个新的集合
List<Integer> list2 = list1.subList(0, 5);
System.out.println("新的集合 list2:" + list2);
System.out.println("list1 是否包含 list2: " + list1.containsAll(list2));
//将集合顺序打乱
Collections.shuffle(list2);
System.out.println("打乱顺序后的集合:" + list2);
//将集合重新排序
Collections.sort(list2);
System.out.println("排序后的集合: " + list2);
//交集操作
list2.retainAll(list1);
System.out.println("list1 和 list2 的交集:" + list2);
//在下标为 2 的位置加一个集合
list2.addAll(2, list2);
System.out.println("在下标为 2 的位置加一个集合:" + list2);
//清空集合
list2.clear();
System.out.println("清空集合:" + list2);
}
打印结果:
list1: [100, 101, 102, 103, 104, 105, 106, 107, 108, 109]
100 的下标:1
删掉第 0 个元素: 100
新的集合 list2:[101, 102, 103, 104, 105]
list1 是否包含 list2: true
打乱顺序后的集合:[105, 103, 102, 104, 101]
排序后的集合: [101, 102, 103, 104, 105]
list1 和 list2 的交集:[101, 102, 103, 104, 105]
在下标为 2 的位置加一个集合:[101, 102, 101, 102, 103, 104, 105, 103, 104, 105]
清空集合:[]
- List 在创建之后,可以通过 indexOf() 方法来获取某个元素下标;
- remove() 方法用来删除某个下标的元素;
- subList() 方法可以从某个集合中创建出一个片段作为一个新的集合,而将这个心机和传递给较大的集合的 containsAll() 方法时,结果肯定为 true;
- 代码中还引用了 Collections.shuffle() 方法用来打乱集合顺序,用 Collections.sort()
方法来对集合重新排序; - retainAll() 方法是一种有效的交集操作,用来获取两个集合的交集;
- addAll() 方法可以在集合的中间插入一个新的集合,这对于 ArrayList 来说,代价较高;
- clear() 方法用来清空集合中的所有元素。
LinkedList
LinkedList 和 ArrayList 一样,也实现了 List 接口,它在 List 中间插入或者移除元素的时候比 ArrayList 更高效,但是随即访问操作的时候要慢一些。LinkedList 还添加了可以使用其作栈、队列或双端队列的方法。
public static void main(String[] args) {
LinkedList<Integer> linkedList = new LinkedList<>();
for (int i = 100; i < 110; i++) {
linkedList.add(i);
}
//集合为空时,以下两个方法报错
System.out.println("第一个元素: " + linkedList.getFirst());
System.out.println("第一个元素: " + linkedList.element());
//集合为空时,返回 null
System.out.println("第一个元素: " + linkedList.peek());
//集合为空时,以下两个方法报错
System.out.println("移除第一个元素:" + linkedList.removeFirst());
System.out.println("移除第一个元素:" + linkedList.remove());
//集合为空时,返回 null
System.out.println("移除第一个元素:" + linkedList.poll());
linkedList.addFirst(1);
System.out.println("在列表前部插入元素:" + linkedList);
linkedList.add(2);
System.out.println("在列表末尾插入元素:" + linkedList);
linkedList.addLast(3);
System.out.println("在列表末尾插入元素:" + linkedList);
linkedList.removeLast();
System.out.println("删除列表末尾的元素:" + linkedList);
}
打印结果:
第一个元素: 100
第一个元素: 100
第一个元素: 100
移除第一个元素:100
移除第一个元素:101
移除第一个元素:102
在列表前部插入元素:[1, 103, 104, 105, 106, 107, 108, 109]
在列表末尾插入元素:[1, 103, 104, 105, 106, 107, 108, 109, 2]
在列表末尾插入元素:[1, 103, 104, 105, 106, 107, 108, 109, 2, 3]
删除列表末尾的元素:[1, 103, 104, 105, 106, 107, 108, 109, 2]
LinkedList 中,getFirst() 和 element() 方法完全一样,都是返回列表的头元素,并且不移除它,如果集合为空,那么抛出 NoSuchElementException 异常,peek() 方法也是获取头元素,但是与前面两个方法稍有差异,它在列表为空的时候返回 null。
removeFirst() 和 remove() 方法也是完全一样,移除并且返回列表的头元素,当列表为空的时候,抛出 NoSuchElementException 异常,poll() 方法也是效果一样,但是列表为空时返回 null。
addFirst() 方法是在列表最前面插入元素,add() 和 addLast() 方法效果一样,是在列表末尾插入元素。removeLast() 方法删除并返回列表的最后一个元素。
小结
本文介绍了 List 接口的两个常用的实现类 ArrayList 和 LinkedList 和它们各自的特点,并通过代码示例简要介绍了这两种集合的常用方法。