使用 List 扩展集合
探索 List 接口
List
接口为普通集合带来了两个新功能。
- 您遍历列表元素的顺序始终相同,并且它遵循元素添加到列表中的顺序。
- 列表中的元素具有索引。
选择 List 接口的实现
虽然 Collection
接口在集合框架中没有特定的实现(它依赖于其子接口的实现),但 List
接口有 2 个:ArrayList
和 LinkedList
。正如您可能猜到的,第一个是基于内部数组构建的,第二个是基于双向链表构建的。
这些实现中有一个比另一个更好吗?如果您不确定选择哪一个,那么您最好的选择可能是 ArrayList
。
在 60 年代计算机发明时,链表的性能优于数组的情况不再存在,并且链表在插入和删除操作方面优于数组的能力,在现代硬件、CPU 缓存和指针追逐的帮助下,已经大大降低了。遍历 ArrayList
元素的速度比遍历 LinkedList
元素快得多,这主要归因于指针追逐和 CPU 缓存未命中。
仍然有一些情况,链表比数组更快。双向链表可以比 ArrayList
更快地访问其第一个和最后一个元素。这是使 LinkedList
比 ArrayList
更好的主要用例。因此,如果您的应用程序需要后进先出 (LIFO,在本教程的后面部分介绍) 堆栈或先进先出 (FIFO,也在后面部分介绍) 等待队列,那么选择链表可能是您最好的选择。
另一方面,如果您计划遍历列表中的元素,或者通过其索引随机访问它们,那么 ArrayList
可能是您最好的选择。
使用索引访问元素
List
接口为 Collection
接口带来了几个处理索引的方法。
访问单个对象
add(index, element)
: 在index
处插入给定对象,如果存在剩余元素,则调整索引get(index)
: 返回给定index
处的对象set(index, element)
: 用新元素替换给定索引处的元素remove(index)
: 删除给定index
处的元素,调整剩余元素的索引。
调用这些方法仅适用于有效索引。如果给定的索引无效,则会抛出 IndexOutOfBoundsException
异常。
查找对象的索引
方法 indexOf(element)
和 lastIndexOf(element)
返回列表中给定元素的索引,如果未找到元素,则返回 -1。
获取子列表
subList(start, end)
返回一个列表,该列表包含索引 start
和 end - 1
之间的元素。如果索引无效,则会抛出 IndexOutOfBoundsException
异常。
请注意,返回的列表是主列表的视图。因此,对子列表的任何修改操作都会反映在主列表上,反之亦然。
例如,您可以使用以下模式清除列表内容的一部分
List<String> strings = new ArrayList<>(List.of("0", "1", "2", "3", "4", "5"));
System.out.println(strings);
strings.subList(2, 5).clear();
System.out.println(strings);
运行此代码将为您提供以下结果
[0, 1, 2, 3, 4, 5]
[0, 1, 5]
插入集合
此列表的最后一个模式是关于在给定索引处插入集合:addAll(int index, Collection collection)
。
对列表中的元素进行排序
列表按已知顺序保留其元素。这是与普通集合的主要区别。因此,对列表中的元素进行排序是有意义的。这就是为什么在 JDK 8 中将 sort()
方法添加到 List
接口中的原因。
在 Java SE 7 及更早版本中,您可以通过调用 Collections.sort()
并将您的列表作为参数传递,以及在需要时传递比较器来对 List
中的元素进行排序。
从 Java SE 8 开始,您可以直接在您的列表上调用 sort()
并将您的比较器作为参数传递。此方法没有不接受任何参数的重载。如果使用空比较器调用它,则将假设您的 List
中的元素实现了 Comparable
,如果您不是这种情况,您将获得 ClassCastException
。
如果您不喜欢使用空参数调用方法(您是对的!),您仍然可以使用 Comparator.naturalOrder()
来调用它以获得相同的结果。
遍历列表中的元素
List
接口为您提供了另一种使用 ListIterator
遍历其元素的方法。您可以通过调用 listIterator()
来获取这样的迭代器。您可以不带任何参数调用此方法,或者向其传递一个整数索引。在这种情况下,迭代将从该索引开始。
ListIterator
接口扩展了您已经知道的常规 Iterator
。它向其中添加了几个方法。
hasPrevious()
和previous()
: 用于以降序而不是升序进行迭代nextIndex()
和previousIndex()
: 用于获取将由下一个next()
调用或下一个previous()
调用返回的元素的索引set(element)
: 用于更新由next()
或previous()
返回的最后一个元素。如果在这两种方法中都没有在该迭代器上调用过,则会引发IllegalStateException
。
让我们看看 set()
方法的实际应用
List<String> numbers = Arrays.asList("one", "two", "three");
for (ListIterator<String> iterator = numbers.listIterator(); iterator.hasNext();) {
String nextElement = iterator.next();
if (Objects.equals(nextElement, "two")) {
iterator.set("2");
}
}
System.out.println("numbers = " + numbers);
运行此代码将为您提供以下结果
numbers = [one, 2, three]
最后更新: 2021年9月14日