系列中的上一篇
当前教程
使用 List 扩展集合

使用 List 扩展集合

 

探索 List 接口

List 接口为普通集合带来了两个新功能。

  • 您遍历列表元素的顺序始终相同,并且它遵循元素添加到列表中的顺序。
  • 列表中的元素具有索引。

 

选择 List 接口的实现

虽然 Collection 接口在集合框架中没有特定的实现(它依赖于其子接口的实现),但 List 接口有 2 个:ArrayListLinkedList。正如您可能猜到的,第一个是基于内部数组构建的,第二个是基于双向链表构建的。

这些实现中有一个比另一个更好吗?如果您不确定选择哪一个,那么您最好的选择可能是 ArrayList

在 60 年代计算机发明时,链表的性能优于数组的情况不再存在,并且链表在插入和删除操作方面优于数组的能力,在现代硬件、CPU 缓存和指针追逐的帮助下,已经大大降低了。遍历 ArrayList 元素的速度比遍历 LinkedList 元素快得多,这主要归因于指针追逐和 CPU 缓存未命中。

仍然有一些情况,链表比数组更快。双向链表可以比 ArrayList 更快地访问其第一个和最后一个元素。这是使 LinkedListArrayList 更好的主要用例。因此,如果您的应用程序需要后进先出 (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) 返回一个列表,该列表包含索引 startend - 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日


系列中的上一篇
当前教程
使用 List 扩展集合