List是一种有序、提供角标、一维数据列表、允许重复元素、允许null元素的集合。它是collection的一个子接口,其已知实现的子类且常用的有ArrayList、LinkedList和Vector。下面将对List的一些方法的使用和它的常用子类的方法的使用进行介绍。
List的方法测试和使用示例以及说明
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class Main {
public static void main(String[] args) {
//list也是一个接口,不能创建对象,只能创建其子类对象
List<Integer> list=new ArrayList<Integer>();
for(int i=0;i<10;i++){
//这里调用的是list自己的add(int index, E element)方法,
//也可以直接添加元素调用add(E e)方法,这是从collection接口继承来的方法
//而这里用到了list提供角标的特性,在指定位置添加元素
list.add(i,i);
}
System.out.println(list);
//结果 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
//调用dd(E e)方法,默认在列表的尾部添加
list.add(10);
System.out.println(list);
//结果 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
//get方法获取某个角标下的值,注意角标越界喔!
System.out.println(list.get(10));
//结果 10
//在该集合中间某角标位置添加一个数,该位置的数并不会被覆盖,
//而是从该位置起的元素到最后的元素都向后移一个位置,再把要添加的数添加进去,哪怕元素重复也会添加进去
//注意:在指定添加位置的时候,位置下标的取值在[(该集合的首元素下标-1)且要大于等于零,该集合的长度]
//如果指定的下标超过这个范围就会发生错误
list.add(4,11);
list.add(4,11);
System.out.println(list);
//结果 [0, 1, 2, 3, 11, 11, 4, 5, 6, 7, 8, 9, 10]
//获得该集合的长度
System.out.println(list.size());
//结果 13
//indexOf方法,从左右往右获得指定元素的第一次出现的位置下标
System.out.println(list.indexOf(11));
//结果 4
//lastIndexOf方法是获得指定元素的最后一次出现的位置下标,相当于是从后往前查找指定元素第一次出现的位置下标
System.out.println(list.lastIndexOf(11));
//结果5
//有两种remove方法
//remove(int index) 移除列表中指定位置的元素
//remove(Object e) 从此列表中移除第一次出现的指定元素
//因为泛型E-Integer,所以的参数容易混淆
//因此如果直接调用remove方法并传入一个值,我们自己可能想移出11这个元素,但是这里会默认是移出角标为11所对应的元素9
System.out.println(list.remove(11));
//结果 9
System.out.println(list);
//结果 [0, 1, 2, 3, 11, 11, 4, 5, 6, 7, 8, 10]
//因此要移出某个元素,必须创建该元素的对象再移出,但是它返回的结果是Boolean类型,表示是否移出成功
System.out.println(list.remove(new Integer(11)));
//结果 true
System.out.println(list);
//结果 [0, 1, 2, 3, 11, 4, 5, 6, 7, 8, 10] ,当然只会移出一个
//set方法用指定元素去替换指定位置的元素,这里是覆盖,元素不会移动
list.set(0, 666);
System.out.println(list);
//结果 [666, 1, 2, 3, 11, 4, 5, 6, 7, 8, 10]
//subList方法用于截取该集合的子串,需要指明从哪个位置开始,截取多少,
//当然这个截取的大小不能超过集合总长,指定的位置不能越过集合的角标范围
//这个方法只是截取复制出来,对原集合不受影响
System.out.println(list.subList(0, 3));
//结果 [666, 1, 2]
System.out.println(list);
//截取后原集合的结果不变 [666, 1, 2, 3, 11, 4, 5, 6, 7, 8, 10]
//list集合也有迭代器Iterator方法,用来遍历集合的元素
//但是Iterator在迭代集合的过程中,对集合是不可进行修改的,即不能调用其相应的方法进行操作
//这段代码虽然编译时不会报错,但是在运行时就会出错ConcurrentModificationException,表示不可修改
/*Iterator<Integer> it=list.iterator();
while(it.hasNext()){
Integer number=it.next();
if(number==3){
//ConcurrentModificationException
list.remove(new Integer(3));
}else{
System.out.println(number);
}
}*/
//为解决这一问题,list接口增加了ListIterator方法,有两种选择:
//可以获取ListIterator()方法,返回此列表元素的列表迭代器(按适当顺序)。
//listIterator(int index)方法 ,返回列表中元素的列表迭代器(按适当顺序),从列表的指定位置开始。
//因此,可以在迭代集合的过程中,通过ListIterator对集合进行修改
ListIterator<Integer> it2=list.listIterator();
while(it2.hasNext()){
Integer number=it2.next();
if(number==11){
//这里的remove()方法是ListIterator内部的方法
it2.remove();
}else{
System.out.println(number);
}
}
//迭代完成的结果 [666, 1, 2, 3, 4, 5, 6, 7, 8, 10]
System.out.println(list);
//修改后的原集合 [666, 1, 2, 3, 4, 5, 6, 7, 8, 10]
//当然list提供角标访问,可以直接用循环访问下标获取集合的元素
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
//结果 [666, 1, 2, 3, 11, 4, 5, 6, 7, 8, 10]
}
}
ArrayList<E>类
ArrayList基于数组实现的、且容量和大小是可变的、但是在线程上是不同步的(线程问题会在后面进行介绍)。
基于数组实现的链表的优点是:查改快;缺点为:增删慢。ArrayList的方法有很多,大多都和它的父接口中的方法用法相同(参照上面List中的方法和collection中的方法),下面是它的一些基本方法的测试和说明。
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
//这里还是采用Integer类来进行测试方便理解。因为ArrayList是一个类,因此可以创建自己的对象
/*
* ArrayList()有三个构造方法:
* ArrayList() 构造一个初始容量为 10 的空列表。
* ArrayList(Collection<? extends E> c)(该方法不常用)
* 构造一个包含指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的。
* ArrayList(int initialCapacity) 构造一个具有指定初始容量的空列表。
*/
//这里创建的是一个指定初始容量为20的空列表对象
ArrayList<Integer> list=new ArrayList<Integer>(20);
for(int i=1;i<=10;i++){
list.add(i); //add方法,将指定的元素添加到此列表的尾部。
}
System.out.println(list);
//结果 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// trimToSize()方法,将此 ArrayList 实例的容量调整为列表的当前大小。
//此方法不能用结果来测试,原因是size()方法是获取当前集合的有效元素的大小
//在ArrayList当中没有获取当前容量的方法,因为这是在内存中处理的,并没有返回结果,因此无法根据结果来测试
list.trimToSize();
//ensureCapacity方法是, 如有必要,增加此 ArrayList 实例的容量,以确保它至少能够容纳最小容量参数所指定的元素数。
//此方法与trimToSize方法相对,当trimToSize方法减少容量时ensureCapacity增加容量(也可以减少)
//虽然在这里,我们看不到结果,但是还是将当前的容量改回来,方便后面的方法测试
list.ensureCapacity(20);
//clone()方法,返回此 ArrayList 实例的浅表副本。相当于把原列表复制一份到新的一个列表
//在复制时,还要进行强制转换,因为clone()方法返回的类型是object对象
ArrayList<Integer> list2=(ArrayList<Integer>) list.clone();
System.out.println(list2);
//复制之后的list2结果 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
//调用equals方法比较两个列表的内容,结果为true
System.out.println(list.equals(list2));
//虽然内容一样,但是它还是两个对象,用==比较两个列表对象的地址,结果为false
System.out.println(list==list2);
//toArray()方法,按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组;
//返回数组的运行时类型是指定数组的运行时类型
Object[] numbers=list.toArray();
//通过toArray方法,将该列表转为数组,然后用遍历数组的方法进行遍历该列表的元素
for(int i=0;i<numbers.length;i++){
System.out.println(numbers[i]);
}
//结果 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}
}
LinkedList<E>类
LinkedList是基于链表实现、但即可当做列表使用、也可当做栈使用,还可当做双端队列使用,且它的线程也是不同步的。
基于链表实现的列表的优点是:增删快;缺点为:查改慢,LinkedList与ArrayList同为List的子类,大多也都和它的父接口中的方法用法相同(参照上面List中的方法和collection中的方法),下面是它的一些基本方法的测试和说明。
import java.util.Iterator;
import java.util.LinkedList;
import java.util.ListIterator;
public class Main {
public static void main(String[] args) {
//LinkedList可用于列表、堆栈和队列使用
LinkedList<Integer> list=new LinkedList<Integer>();
//当做列表使用
for(int i=0;i<10;i++){
list.add(i); //add方法,将指定元素添加到此列表的结尾。
}
System.out.println(list);
//结果 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
//clear方法,从此列表中移除所有元素。
list.clear();
System.out.println(list);
//结果 []
for(int i=0;i<10;i++){
//addFirst方法,将指定元素插入此列表的开头。还有一个addLast方法, 将指定元素添加到此列表的结尾。
//此方法既可以当做列表使用,也可当做堆栈使用
list.addFirst(i);
}
System.out.println(list);
//结果 [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
list.clear();
//当做堆栈使用
for(int i=0;i<10;i++){
//push方法,将元素推入此列表所表示的堆栈。
list.push(i);
}
System.out.println(list);
//结果 [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
list.clear();
//当做队列使用
for(int i=0;i<10;i++){
//offer方法, 将指定元素添加到此列表的末尾(最后一个元素)。
//与列表使用相同,还有offerFirst方法,在此列表的开头插入指定的元素
//offerLast方法,在此列表末尾插入指定的元素。
list.offer(i);
}
System.out.println(list);
//结果 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
//get方法,返回此列表中指定位置处的元素。此外还有:
//getFirst():返回此列表的第一个元素。
//getLast():返回此列表的最后一个元素
System.out.println(list.get(3));
//结果 3
//在LinkedList中,提供descendingIterator()方法,返回以逆向顺序在此双端队列的元素上进行迭代的“迭代器”。
//默认从表尾开始遍历
Iterator<Integer> it=list.descendingIterator();
while(it.hasNext()){
System.out.print(it.next());
}
//结果 9876543210
System.out.println();
//除此之外,LinkedList也存在listIterator(int index)方法,
//返回此列表中的元素的“列表迭代器”(按适当顺序),从列表中指定位置开始。可以默认从表头开始遍历或者指定起始位置开始
//在ListIterator<E>接口中,比Iterator<E>接口多了一种遍历的方法,逆序遍历
//previous(): 返回列表中的前一个元素。
//hasPrevious(): 如果以逆向遍历列表,列表迭代器有多个元素,则返回 true。
//如果默认从表头开始 则调用previous方法没元素,只能用next方法
//如果从指定位置开始,可以用previous方法,也可以用next方法
ListIterator<Integer> it2=list.listIterator(5);
while(it2.hasPrevious()){
System.out.print(it2.previous());
}
//结果 43210
//注意:Iterator 只能从表头开始 且只能用next
}
}
Vector<E>类
Vector是基于数组的、可变长的、线程同步的集合、(同步意味着线程安全->有函数锁->每次调用函数得先判断锁是否被占用,一般在多线程的情况下用和StringBuffer比较像),缺点是都慢,但是一般这个借口不常用,常用的是它的实现子类Stack,基于栈实现,Vector的改装。关于Stack的介绍和方法的使用,在前面已经介绍过,请参考:
https://blog.csdn.net/weixin_45432742/article/details/99850913
至此有关List的子类和方法的使用就介绍到这里。现在我们来总结一下迭代器,如图: