List集合

List集合类中元素有序、且可重复,集合中的每个元素都有其对应的顺序索引,List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。

(1)List集合的用法

List集合包括List接口以及List接口的所有实现类。因为List接口继承了Collection接口,所以List接口拥有Collection接口提供的所有常用方法,又因为List是列表类型,所以List接口还提供了一些适合于自身的常用方法,如下表2所示:



从上表可以看出,List接口提供的适合于自身的常用方法均与索引有关,这是因为List集合为列表类型,以线性方式存储对象,可以通过对象的索引操作对象。

List接口的常用实现类有ArrayList和LinkedList,在使用List集合时,通常情况下声明为List类型,实例化时根据实际情况的需要,实例化为ArrayList或LinkedList,例如:

List<String> l1 = new ArrayList<String>();//利用ArrayList类实例化List集合

List<String> l2 = new LinkedList<String>();//利用LinkedList类实例化List集合

注意:

ArrayList 是线程不安全的,而 Vector 是线程安全的,即使为保证 List 集合线程安全,Vector效率低也不推荐使用。

(1.1)add(int index,Object obj)方法和set(int index,Object obj)方法的区别

在使用List集合时需要注意区分add(int index,Object obj)方法和set(int index,Object obj)方法,前者是向指定的索引位置添加对象,而后者是修改指定的索引位置的对象。

例如:

import java.util.*;  
public class TestList{
public static void main(String args[]){
String a = "A" , b = "B" , c = "C" , d = "D" , e = "E";
List<String> list = new LinkedList<String>();
list.add(a);
list.add(e);
list.add(d);
list.set(1,b);//将索引位置为1的对象e修改为对象b
list.add(2,c);//将对象c添加到索引位置为2的位置
Iterator<String> it = list.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}


程序的运行结果如下:

A

B

C

D

因为List集合可以通过索引位置访问对象,所以还可以通过for循环遍历List集合,例如遍历上面代码中的List集合的代码如下:

for(int i=0;i<list.size();i++){  
System.out.println(list.get(i));//利用get(int index)方法获得指定索引位置的对象
}


(1.2)indexOf(Object obj)方法和lastIndexOf(Object obj)方法的区别

在使用List集合时需要注意区分index(Object obj)方法和lastIndexOf(Object obj)方法,前者是获得指定对象的最小的索引位置,而后者是获得指定对象的最大的索引位置。前提条件是指定的对象在List集合中具有重复的对象,如果在List集合中有且仅有一个指定的对象,则通过这两个方法获得的索引位置是相同的。

例如:

String a = "A" , b = "B" , c = "C" , d = "D" , repeat = "Repeat";  
List<String> list = new ArrayList<String>();
list.add(a); //索引位置为0
list.add(repeat); //索引位置为1
list.add(b); //索引位置为2
list.add(repeat); //索引位置为3
list.add(c); //索引位置为4
list.add(repeat); //索引位置为5
list.add(d); //索引位置为6
System.out.println(list.indexOf(repeat));
System.out.println(list.lastIndexOf(repeat));
System.out.println(list.indexOf(b));
System.out.println(list.lastIndexOf(b));


程序的运行结果如下:

1

5

2

2

(1.3)subList(int fromIndex,int toIndex)方法

在使用subList(int fromIndex,int toIndex)方法截取现有List集合中的部分对象生成新的List集合时,需要注意的是,新生成的集合中包含起始索引位置代表的对象,但是不包含终止索引位置代表的对象。

例如:

String a = "A" , b = "B" , c = "C" , d = "D" , e = "E";  
List<String> list = new ArrayList<String>();
list.add(a);
list.add(b);
list.add(c);
list.add(d);
list.add(e);
list = list.subList(1,3);
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}


程序的运行结果如下:

B

C


(2)使用ArrayList类

ArrayList类实现了List接口,由ArrayList类实现的List集合采用数组结构保存对象。数组结构的优点是便于对集合进行快速的随机访问,如果经常需要根据索引位置访问集合中的对象,ArrayList采用的是数组,数组有明确的下标,所以查找很快,使用由ArrayList类实现的List集合的效率较好。数组结构的缺点是向指定索引位置插入对象和删除指定索引位置对象的的速度较慢,如果经常需要向List集合的指定索引位置插入对象,或者是删除List集合的指定索引位置的对象,使用由ArrayList类实现的List集合的效率则较低,并且插入或删除对象的索引位置越小效率越低,原因是当向指定的索引位置插入对象时,会同时将指定索引位置及之后的所有对象相应的向后移动一位,如图2所示。当删除指定索引位置的对象时,会同时将指定索引位置之后的所有对象相应的向前移动一位,如图3所示。如果在指定的索引位置之后有大量的对象,将严重影响对集合操作效率。




下面看一个模仿经常需要随机访问集合中对象的例子:

例如:

String a = "A" , b = "B" , c = "C" , d = "D" , e = "E";  
List<String> list = new ArrayList<String>();
list.add(a);
list.add(b);
list.add(c);
list.add(d);
list.add(e);
System.out.println(list.get((int)(Math.random()*5)));


程序运行结果是随机的。


(3)使用LinkedList类

LinkedList类实现了List接口,用LinkedList类实现的List集合采用链表结构保存对象。链表结构的优点是便于向集合中插入和删除对象,如果经常需要向集合中插入对象,或者是从集合中删除对象,使用由LinkedList类实现的List集合的效率较好。链表结构的缺点是随机访问对象的速度较慢,如果经常需要随机访问集合中的对象,LinkedList采用的是链表,每个节点都会保存前一个和后一个节点。通过循环,把当前节点指向上一个或者下一个,来找到索引位置的节点,使用LinkedList类实现的List集合的效率较低,。用LinkedList类实现的List集合便于插入或删除对象的原因是当插入或删除对象时,只需要简单的修改链接位置,分别如图4和图5所示,省去了移动对象的操作。




LinkedList类还根据采用链表结构保留对象的特点,提供了几个专有的操作集合的方法,如下表3所示:



例如:

String a = "A" , b = "B" , c = "C" , test = "Test";  
LinkedList<String> list = new LinkedList<String>();
list.add(a);//索引位置为0
list.add(b);//索引位置为1
list.add(c);//索引位置为2
System.out.println(list.getFirst());//获得并输出链表开头的对象
list.addFirst(test);//向链表的开头添加一个对象
System.out.println(list.getFirst());//获得并输出链表开头的对象
list.removeFirst();//移除链表开头的对象
System.out.println(list.getFirst());//获得并输出链表开头的对象


程序的运行结果如下:

A

Test

A


(4)客户化排序List集合

在使用List集合时,通常情况下希望从集合中得到的对象是按照一定顺序排列的,但是List集合的默认排序方式为按照对象的插入顺序,可以通过Java.util.Collections类的静态方法sort(List<T> list)、sort(List<T> list,Comparator<? super T> c)或reverse(List<?> list)对集合中的对象进行客户化排序,其中方法sort(List<T> list)和reverse(List<?> list)要求集合中的对象必须实现java.lang.Comparable接口,即实现方法compareTo(),该方法的具体定义如下:

public int compareTo(T o);

方法sort(List<T> list)是将集合中的所有对象按正序排列,方法reverse(List<?> list)是将集合中的所有对象按倒序排列;方法sort(List<T> list,Comparator<? super T> c)不要求集合中的对象必须实现Comparable接口,但是在使用该方法时需要显式设置比较器,即该方法的第2个入口参数,比较器必须实现java.util.Comparator接口,即实现方法compare(),该方法的就具体定义如下:

int compare(T o1,T o2);

比较器的功能是实现对集合中所有对象的排序策略。

注意:List集合进行客户化排序的前提条件是List集合中的元素为同一类型。

1、通过实现java.lang.Comparable接口实现客户化排序

例如:

源文件:CnToSpell.java(源文件见下载处CnToSpell.rar压缩文件)

源文件:Person.java

public class Person implements Comparable{  
private String name;
private long id_card;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public long getId_card(){
return id_card;
}
public void setId_card(long id_card){
this.id_card = id_card;
}
public int compareTo(Object o){//实现Comparable接口的方法
Person p = (Person)o;
String s1 = CnToSpell.getFullSpell(this.name);//获得汉字的全拼
String s2 = CnToSpell.getFullSpell(p.getName());
return s1.compareTo(s2);//比较两个字符串的大小
}
}


源文件:TestList.java

import java.util.*;  
public class TestList{
public static void main(String args[]){
List<Person> list = new ArrayList<Person>();
String names[] = {"马先生","王×××","李先生"};
long id_cards[] ={22015,22020,22018};
for(int i=0;i<names.length;i++){//初始化List集合
Person person = new Person();
person.setName(names[i]);
person.setId_card(id_cards[i]);
list.add(person);
}
System.out.println("排序前:");
for(int i=0;i<list.size();i++){//遍历List集合
Person person = list.get(i);
System.out.println("-----"+person.getName()+" "+person.getId_card());
}
//利用java.util.Collections类的sort(List list)或reverse(List list)方法对List集合排序
Collections.sort(list);//按升序排序
System.out.println("升序排列后:");
for(int i=0;i<list.size();i++){//遍历List集合
Person person = list.get(i);
System.out.println("-----"+person.getName()+" "+person.getId_card());
}
Collections.reverse(list);//按降序排列
System.out.println("降序排列后:");
for(int i=0;i<list.size();i++){//遍历List集合
Person person = list.get(i);
System.out.println("-----"+person.getName()+" "+person.getId_card());
}
}
}


程序的运行结果如下:

排序前:

-----马先生     22015

-----王×××     22020

-----李先生     22018

升序排列后:

-----李先生     22018

-----马先生     22015

-----王×××     22020

降序排列后:

-----王×××     22020

-----马先生     22015

-----李先生     22018

利用这种方式实现对List集合进行客户化排序,缺点是对于每个类只能采用一种排序方式,对于排序方式需求单一的对象,可采用该种方式。

2、通过实现java.util.Comparator接口实现客户化排序

例如:

源文件:Person.java

public class Person{  
private String name;
private long id_card;
public long getId_card(){
return id_card;
}
public void setId_card(long id_card){
this.id_card = id_card;
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
}


源文件:PersonComparator.java

import java.util.Comparator;  
public class PersonComparator implements Comparator{
//为可能参与排序的属性定义同名的静态常量值
public static final int NAME = 1;
public static final int ID_CARD = 2;
private int orderByColumn = 1;//默认排序为按姓名排序
public int compare(Object o1,Object o2){//实现Comparator接口的方法
Person p1 = (Person)o1;
Person p2 = (Person)o2;
int result = 0;//默认的判断结果为两个对象相等
switch(orderByColumn){//判断排序条件
case 1://按姓名排序
String s1 = CnToSpell.getFullSpell(p1.getName());//获得汉字的全拼
String s2 = CnToSpell.getFullSpell(p2.getName());
result = s1.compareTo(s2);//比较两个字符串的大小
break;
case 2:
result = (int)(p1.getId_card()-p2.getId_card());//比较两个整数的大小
break;
}
return result;
}
public void orderByColumn(int orderByColumn){//用来设置排序条件
this.orderByColumn = orderByColumn;
}
}


源文件:TestList.java

public class TestList{  
public static void main(String args[]){
List<Person> list = new ArrayList<Person>();
String names[] ={"马先生","王×××","李先生"};
long id_cards[] = {22015,22020,22018};
for(int i= 0;i<names.length;i++){
Person person = new Person();
person.setName(names[i]);
person.setId_card(id_cards[i]);
list.add(person);
}
System.out.println("排序前:");
for(int i=0;i<list.size();i++){
Person person = list.get(i);
System.out.println("-----"+person.getName()+" "+person.getId_card());
}
PersonComparator personComparator = new PersonComparator();//创建比较器对象
System.out.println("按姓名排序:");
Collections.sort(list,personComparator);//默认为按姓名排序,排序List集合
for(int i=0;i<list.size();i++){
Person person = list.get(i);
System.out.println("-----"+person.getName()+" "+person.getId_card());
}
System.out.println("按编号排序:");
personComparator.orderByColumn(PersonComparator.ID_CARD);//设置为按编号排序
Collections.sort(list,personComparator);
for(int i=0;i<list.size();i++){
Person person = list.get(i);
System.out.println("-----"+person.getId_card()+" "+ person.getName());
}
}
}


程序的运行结果如下:

排序前:

-----马先生     22015

-----王×××     22020

-----李先生     22018

按姓名排序:

-----李先生     22018

-----马先生     22015

-----王×××     22020

按编号排序:

-----22015      马先生

-----22018      李先生

-----22020      王×××

利用这种方式实现对List集合进行客户化排序,排除了每个类只能采用一种排序方式的弊端,可以根据实际需要,将List集合按照不同的方式排序。这里是按姓名的全称升序排列,如果想改为降序排列,只需将例子中的的如下代码:

result = s1.compareTo(s2);

修改改为:

result = s2.compareTo(s2);

编号也是按升序排列的,如果也想改为降序排列,只需将例子中的如下代码:

result = (int)(p1.getId_card()-p2.getId_card());

修改为:

result = (int)(p2.getId_card()-p1.getId_card());