今天学习的内容是集合类之List接口
Collection接口有两个子接口:List接口与Set接口。List接口的实现类统称为List集合(列表),而Set接口的实现类统称为Set集合(集)。在实际开发中,要根据不同集合的特点,选择到底使用哪种集合:
- 列表的特点:元素允许重复;有序,元素的顺序就是添加的顺序
- 集的特点:元素不能重复;元素可能是无序的
实际上,集也可以是有序的(LinkedHashSet与TreeSet),所以在开发中选择列表或集的依据只有一个:是否需要重复元素!
List接口在Collection接口的基础上添加了许多特有方法,使得可以在列表的任意位置对元素进行增删改查(原因是列表建立了数组索引与对象的关联,而集却没有),常用方法如下:
- add(int index, E e) 在列表的指定位置插入指定元素
- addAll(int index, Collection<? extends E> c) 在列表的指定位置插入指定集合的所有元素
- remove(int index) 删除指定位置的元素,并返回该元素
- indexOf(Object o) 返回列表第一次出现指定元素的索引,如果列表不包含此元素,则返回 -1
- lastIndexOf(Object o) 返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 -1
- get(int index) 返回列表中指定位置的元素
- set(int index, E e) 用指定元素替换列表中指定位置的元素
- subList(int fromIndex, int toIndex) 返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分列表
- listIterator() 获取列表迭代器对象(只能用于列表,可以进行反向迭代,也可以在迭代过程中改变列表)
使用上述方法的示例程序:
public class Test52 {
public static void main(String[] args){
List<String> list = new ArrayList<String>();
show_1(list);
show_2(list);
}
public static void show_2(List<String> list) {
//获取列表所有元素的方法:使用迭代器或者get(index)方法
// Iterator<String> it = list.iterator();
// while(it.hasNext()){
// System.out.println("迭代器方法:"+it.next());
// }
for (int i = 0; i < list.size(); i++) {
System.out.println("get()方法:"+list.get(i));
}
//在迭代过程中改变列表,要使用列表迭代器(只有列表具备该功能)
ListIterator<String> lit = list.listIterator();
while(lit.hasNext()){//列表迭代器还可以反向迭代
String str = lit.next();
if(str.equals("kkk")){
//!list.add("incredible!!");//在迭代过程中改变了列表,会发生ConcurrentModificationException
lit.set("kkk incredible!!");
}else if(str.equals("ab")){
lit.add("yes");
}
}
System.out.println(list);//[ab, yes, kkk incredible!!, 2, 3, ab, yes, cd, ef]
}
public static void show_1(List<String> list) {
//列表允许添加重复元素
list.add("ab");
list.add("ab");
list.add("cd");
list.add("ef");
System.out.println("列表:" + list + " 列表长度:" + list.size());//列表:[ab, ab, cd, ef] 列表长度:4
//插入元素
list.add(1,"kk");
System.out.println("列表:" + list + " 列表长度:" + list.size());//列表:[ab, kk, ab, cd, ef] 列表长度:5
List<String> l = new ArrayList<String>();
l.add("1");
l.add("2");
l.add("3");
list.addAll(2, l);
System.out.println("列表:" + list + " 列表长度:" + list.size());//列表:[ab, kk, 1, 2, 3, ab, cd, ef] 列表长度:8
//删除元素
list.remove(2);
System.out.println("列表:" + list + " 列表长度:" + list.size());//列表:[ab, kk, 2, 3, ab, cd, ef] 列表长度:7
//获取指定元素的下标
int a = list.indexOf("ab");
int b = list.lastIndexOf("ab");
System.out.println("a="+a+",b="+b);//a=0,b=4
//根据下标获取元素
String str = list.get(1);
System.out.println("列表中下标为1的元素为:"+str);//列表中下标为1的元素为:kk
//更改指定位置的元素
list.set(1, "kkk");
System.out.println("列表:" + list + " 列表长度:" + list.size());//列表:[ab, kkk, 2, 3, ab, cd, ef] 列表长度:7
//获取子列表
List<String> subList = list.subList(0, 3);
System.out.println("子列表:" + subList + " 子列表长度:" + subList.size());//子列表:[ab, kkk, 2] 子列表长度:3
}
}
常用的列表包括Vector、ArrayList和LinkedList,大概介绍一下它们的区别:
1.Vector:底层数据结构是数组,实现了可变数组,是同步的
缺点:向列表中间插入、删除元素和随机访问列表中元素的速度都较慢
2.ArrayList:底层数据结构是数组,实现了可变数组,是不同步的,替代了Vector
缺点:向列表中间插入、删除元素的速度较慢(牵一发动全身)
优点:随机访问列表中元素的速度较快(数组在内存中是连续的)
3.LinkedList:底层数据结构是链表,是不同步的
缺点:随机访问列表中元素的速度较慢(链表在内存中是不连续的)
优点:向列表中间插入、删除元素的速度较快(修改对象中存储的内存地址即可)
总结:如果要向列表中间频繁地插入和删除元素,建议使用LinkedList;如果要进行大量的随机访问,就使用ArrayList;程序中不应该使用过时的Vector
面试题:
1.什么是迭代器Iterator?
答:Iterator接口提供了很多对集合元素进行迭代的方法。每一个集合类都包含了可以返回迭代器实例的
迭代方法。迭代器可以在迭代的过程中删除底层集合的元素,但是不可以直接调用集合的
remove(Object Obj)删除,可以通过迭代器的
remove()方法删除。
2.Iterator和ListIterator的区别是什么?
答:Iterator可用来遍历Set和List集合,但是ListIterator只能用来遍历List。
Iterator对集合只能是前向遍历,ListIterator既可以前向也可以后向。
ListIterator实现了Iterator接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等。
Iterator对集合只能是前向遍历,ListIterator既可以前向也可以后向。
ListIterator实现了Iterator接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等。
3.阐述ArrayList、Vector、LinkedList的存储性能和特性。
答:ArrayList 和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector中的方法由于添加了synchronized修饰,因此Vector是线程安全的容器,但性能上较ArrayList差,因此已经是Java中的遗留容器。LinkedList使用双向链表实现存储(将内存中零散的内存单元通过附加的引用关联起来,形成一个可以按序号索引的线性结构,这种链式存储方式与数组的连续存储方式相比,内存的利用率更高),按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。但是由于ArrayList和LinkedListed都是非线程安全的,如果遇到多个线程操作同一个容器的场景,则可以通过工具类Collections中的synchronizedList方法将其转换成线程安全的容器后再使用(这是对装潢模式的应用,将已有对象传入另一个类的构造器中创建新的对象来增强实现)