Java集合框架之List

本文详细介绍了Java中的List容器,包括ArrayList和LinkedList的特点与区别,以及如何使用这两种容器进行数据的存储和检索。文章还深入探讨了两种List的具体实现原理及其常用的API方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.基本概念

Java容器类类库的用途是“保存对象”,并将其划分为两个不同的概念。
1.Collection。一个独立元素的序列,这些元素都服从一条或多条规则。List必须按照插入的顺序保存元素,而Set不能有重复元素。Queue按照排队规则来确定对象产生的顺序(通常与它们被插入的顺序相同)
2.Map。一组成对的“键值对”对象,允许你使用键来查找值。ArrayList允许你使用数字来查找值,因此在某种意义上,它将数字与对象关联在了一起。而Map允许我们使用另外一个对象来查找对象,我们称之为“关联数组”,或者与在Python中类似,也可称之为“字典”。

容器分类图如下所示:

容器分类图

2.List

List承诺可以将元素维护在特定的序列中。ListCollection的一个子接口,它在Collection的基础上增加了大量的方法,使得可以在List的中间插入和移除元素。

有两种类型的List
- 基本的ArrayList,它长于随机访问元素,但是在ArrayList的中间插入和移除元素时较慢。
- LinkedList,它通过代价较低的在List中间进行的插入和删除操作,提供了优化的顺序访问。LinkedList在随机访问方面相对比较慢,但是它的特性集较ArrayList更大。

List接口中定义的一些常用的抽象方法有:

序号方法描述
1boolean add(E e)将指定的元素添加到此列表的尾部
2void add(int index, E element)将指定的元素插入此列表中的指定位置
3boolean addAll(Collection<? extends E> c)按照指定 collection 的迭代器所返回的元素顺序,将该 collection 中的所有元素添加到此列表的尾部
4boolean addAll(int index, Collection<? extends E> c)从指定的位置开始,将指定 collection 中的所有元素插入到此列表中
5E set(int index, E element)用指定的元素替代此列表中指定位置上的元素
6E get(int index)返回此列表中指定位置上的元素
7int indexOf(Object o)返回此列表中首次出现的指定元素的索引,或如果此列表不包含元素,则返回 -1
8int lastIndexOf(Object o)返回此列表中最后一次出现的指定元素的索引,或如果此列表不包含索引,则返回 -1
9E remove(int index)移除此列表中指定位置上的元素
10boolean remove(Object o)移除此列表中首次出现的指定元素(如果存在)
11boolean removeAll(Collection<?> c)从列表中移除指定 collection 中包含的其所有元素
12void clear()移除此列表中的所有元素
13boolean isEmpty()如果此列表中没有元素,则返回 true
14int size()返回此列表中的元素数
15Object[] toArray()按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组
16List<E> subList(int fromIndex, int toIndex)返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图
17boolean retainAll(Collection<?> c)仅在列表中保留指定 collection 中所包含的元素(可选操作)
18Iterator<E> iterator()返回按适当顺序在列表的元素上进行迭代的迭代器
19ListIterator<E> listIterator()返回此列表元素的列表迭代器
20ListIterator<E> listIterator(int index)返回列表中元素的列表迭代器,从列表的指定位置开始

也就是说,List接口的两个实现类ArrayListLinkedList中,这些方法都是有的。

2.1 ArrayList

通过查看源代码,可以看到ArrayList底层采用数组实现,并且默认的初始长度为10。由于通过数组实现,故ArrayList长于随机访问也就不难理解了。

2.1.1 构造方法

ArrayList中的常用方法与List接口中定义的方法基本一致。它有3个不同的构造方法,如下:

序号构造方法描述
1public ArrayList()构造一个初始容量为 10 的空列表
2public ArrayList(int initialCapacity)构造一个具有指定初始容量的空列表。
3ArrayList(Collection<? extends E> c)构造一个包含指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的。

2.1.2 ArrayList的遍历方式

(1)随机访问,通过索引值去遍历
由于ArrayList实现了RandomAccess接口,它支持通过索引值去随机访问元素。

ArrayList<Integer> array = new ArrayList<>();
array.addAll(Arrays.asList(1, 2, 3));
for (int i = 0; i < array.size(); i++) {
    System.out.println(array.get(i));
}

(2)foreach循环

ArrayList<Integer> array = new ArrayList<>();
array.addAll(Arrays.asList(1, 2, 3));
for(Integer item:array){
    System.out.println(item);
}

(3)迭代器
1.Iterator
通常意义上的Iterator,对Collection进行迭代的迭代器。它也是一个对象,它的工作时遍历并选择序列中的对象,而程序员不必知道或关心该序列底层的结构。
Java的Iterator只能单向移动,这个Iterator只能用来:

1.使用方法iterator()要求容器返回一个IteratorIterator将准备好返回序列的第一个元素。
2.使用next()获得序列的下一个元素。
3.使用hasNext()检查序列中是否还有元素。
4.使用remove()将迭代器新近返回的元素删除。

使用迭代器遍历ArrayList如下:

ArrayList<Integer> array = new ArrayList<>();
array.addAll(Arrays.asList(1, 2, 3));
Iterator<Integer> iterator = array.iterator();
while(iterator.hasNext()){
    System.out.println(iterator.next());
}

2.ListIterator
ListIterator是一个更加强大的Iterator的子类型,它只能用于各种List类的访问。Iterator只能单向移动,但是ListIterator可以双向移动。它还可以产生相对于迭代器在列表中指向的当前的前一个和后一个元素的索引,并且可以使用set()方法替换它访问过的最后一个元素。我们可以通过调用listIterator()方法产生一个指向List开始位置的ListIterator,并且还可以通过调用listIterator(n)方法创建一个一开始就指向列表索引为n的元素处的ListIterator

ArrayList<Integer> array = new ArrayList<>();
array.addAll(Arrays.asList(1, 2, 3));
// 先从前向后遍历
ListIterator<Integer> listIt = array.listIterator();
while(listIt.hasNext()){
    System.out.println(listIt.next()+", "+listIt.previousIndex()+", "+listIt.nextIndex());
}
System.out.println();
// 再从后向前遍历
while(listIt.hasPrevious()){
    System.out.println(listIt.previous());
}

2.2 LinkedList

LinkedList也像ArrayList一样实现了基本的List接口,但是它底层采用链表实现,所以执行插入和删除的操作比ArrayList更高效,在随机访问操作方面要逊色一些。
LinkedList还添加了可以使其用作栈、队列或者双端队列的方法。这些方法中有些彼此之间只是名称有些诧异,或者只存在些许诧异。比如:

  • (1)getFirst()element()完全一样,它们都返回列表的头(第一个元素),并不移除它,如果List为空,则抛出NoSuchElementExceptionpeek()方法与这两个方式稍有诧异,它在列表为空时返回null
  • (2)removeFirst()remove()也是完全一样的,它们移除并返回列表的头,而在列表为空时抛出NoSuchElementExceptionpoll()稍有差异,它在列表为空时返回null
  • (3)add()addLast()相同,它们都将某个元素插入到列表的尾部。

我个人的习惯,不管是将LinkedList作为队列Queue来使用,还是作为栈Stack来使用,我都使用getFirst()getLast()addFirst()addLast()removeFirst()removeLast()六个方法来实现相关操作,因为这些方法不仅好记,而且意义很明显,可以根据自己需要搭配使用。

另外,LinkedList的遍历方式与ArrayList完全一致,这里不再赘述。


对于另外的两个List的实现类:VectorStack,根据《Think in Java》中所述,我们在现在的新程序中,应该抛弃这两个过时的类。
但是要知道的是,Vector非常类似于ArrayList,两者的用法几乎一致,但是Vector是线程安全的,而ArrayList是非线程安全的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值