Java集合之List
前言
本篇文章是集合框架的第二篇(第一篇文章可参考集合框架(一)),主要内容为:List接口的特点、List接口的常用方法、List接口的常用子类。重点部分依旧会用红色字体标识。
正文
List的特点?
-
jdk文档对List的描述中,首先映入眼帘的两个字就是"有序"。这个有序具体指的是数据存入集合中的顺序与把数据从集合中取出来的顺序一致,而不是指升序或者降序。
-
List具有"索引",所以我们可以对列表中每个元素的插入位置进行精确地控制。
-
列表通常允许重复的元素。
List的常见方法?
上面说过List具有索引,所以List特有的一些常见方法都有一个共性特点:可以操作索引。
// List可以对元素进行增删改查
1、添加
void add(index, element);
void add(index, collection);
2、删除;
Object remove(index): // 注:返回的是对象
3、修改:
Object set(index, element);
4、获取:
Object get(index); // 还可使用iterator进行获取
int indexOf(object);
int lastIndexOf(object);
List subList(from, to);
我们要注意下面的代码:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test {
public static void main(String[] args) {
List list = new ArrayList();
list.add("abc1");
list.add("abc2");
list.add("abc3");
Iterator it = list.iterator(); // 1
while(it.hasNext()) {
Object obj = it.next(); // ConcurrentModificationException
if(obj.equals("abc2")) {
list.add("abc9"); // 2
}
else {
System.out.println("next:" + obj);
}
}
}
}
jdk文档对上面的ConcurrentModificationException是这样描述的:当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。我们分析下上面的代码:代码1处的迭代器对象是根据有3个元素的list集合获取的,而代码2处希望向list集合中再添加一个元素,在迭代器对象操作集合对象的同时集合也在对这些对象进行操作,所以报错。解决方案如下:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class Test {
public static void main(String[] args) {
List list = new ArrayList();
list.add("abc1");
list.add("abc2");
list.add("abc3");
System.out.println("list:" + list);
ListIterator it = list.listIterator(); // 它可以实现在迭代过程中对元素的增删改查并且还可以向前迭代。注:只有list具有该方法。
while(it.hasNext()) {
Object obj = it.next();
if(obj.equals("abc2")) {
it.add("abc9");
// it.set("abc9");
}
}
System.out.println("list:" + list);
}
}
List的常用子类?
关于这一部分我们的讲解思路是:先明确各自的特点,知道具体的场景该用哪一种;再分析各自所具有的一些方法。这两者都可以通过查看jdk文档获悉。
ArrayList?
ArrayList内部的数据结构是数组,它通过50%延长实现可变长度数组;它从jdk1.2才开始出现,它不是同步的,并且它的出现替代了Vector;由于数组所具有的特性(内存中的空间连续),它进行查询操作的速度很快。
ArrayList一般用于存储自定义对象,如下:
import java.util.ArrayList;
import java.util.Iterator;
class Person {
private String name;
private int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String toString(){
return name+":"+age;
}
}
class Demo {
public static void main(String[] args) {
ArrayList al = new ArrayList();
al.add(new Person("lisi1", 21));
al.add(new Person("lisi2", 22));
al.add(new Person("lisi3", 23));
al.add(new Person("lisi4", 24));
Iterator it = al.iterator();
while(it.hasNext()){
Person p = (Person) it.next(); // 此处要注意进行强制转换
System.out.println(p.getName() + ":" + p.getAge());
}
}
}
ArrayList中有一个ensureCapacity(),当我们向ArrayList中添加大量元素之前就可以调用此方法,它可以减少增量重新分配的次数。
import java.util.*;
public class Test {
public static void main(String[] args) {
ArrayList<Object> list = new ArrayList<Object>();
final int N = 10000000;
list = new ArrayList<Object>();
long startTime1 = System.currentTimeMillis();
list.ensureCapacity(N);
for (int i = 0; i < N; i++) {
list.add(i);
}
long endTime1 = System.currentTimeMillis();
System.out.println("使用ensureCapacity方法后:"+(endTime1 - startTime1));
}
}
import java.util.*;
public class Test {
public static void main(String[] args) {
ArrayList<Object> list = new ArrayList<Object>();
final int N = 10000000;
long startTime = System.currentTimeMillis();
for (int i = 0; i < N; i++) {
list.add(i);
}
long endTime = System.currentTimeMillis();
System.out.println("使用ensureCapacity方法前:"+(endTime - startTime));
}
}
LinkedList?
LinkedList内部的数据结构是双向链表;它也是不同步的;由于链表所具有的特性(内存中的空间不连续),它进行增删操作的速度很快。
关于LinkedList方法的使用我们需要注意以下几点:
- LinkedList的addXxx()和removeXxx(),见下:
import java.util.LinkedList;
class Demo {
public static void main(String[] args) {
LinkedList link = new LinkedList();
link.addFirst("abc1");
link.addFirst("abc2");
link.addFirst("abc3");
link.addFirst("abc4");
System.out.println(link);
System.out.println(link.getFirst()); // 获取第一个但不删除。
System.out.println(link.getFirst());
System.out.println();
System.out.println(link.removeFirst()); // 获取第一个并且删除。
System.out.println(link.removeFirst());
System.out.println(link);
}
}
有了上面的代码,我们就可以用LinkedList的addFirst()和removeFirst()[或addLast()和removeLast()]实现堆栈数据结构,用addFirst()和removeLast()[或addLast()和removeFirst()]实现队列数据结构。
- 从jdk1.6版本开始,LinkedList新增了offerXxx()、peekXxx()、pollXxx(),并且它们三者的功能分别相当于addXxx()、getXxx()、removeXxx()。区别就在于当LinkedList里没有元素时,getXxx()、removeXxx()会抛出"NoSuchElementException",而peekXxx()、pollXxx()返回null,这意味着我们可以在操作这些方法之前进行判断。
Vector?
Vector内部的数据结构也是数组,它通过100%延长实现可变长度数组;它从jdk1.0就有了,它是同步的;它进行增删和查询操作都很慢。
关于Vector方法的使用我们需要注意的是Enumeration接口:
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Vector;
class Demo {
public static void main(String[] args) {
Vector v = new Vector();
v.addElement("abc1");
v.addElement("abc2");
v.addElement("abc3");
v.addElement("abc4");
Enumeration en = v.elements();
while (en.hasMoreElements()) {
System.out.println("nextelment:" + en.nextElement());
}
}
}
Enumeration接口的功能与Iterator接口的功能是重复的。此外,Iterator接口添加了一个可选的移除操作,并使用较短的方法名。新的实现应该优先考虑使用Iterator接口而不是Enumeration接口。