Java的集合框架中,`List`接口是一个非常基础且核心的组成部分。它继承自`Collection`接口,代表一个有序的元素序列,并且允许存储重复的元素。与Set集合的无序、不重复特性形成鲜明对比。`List`接口为我们提供了一套通过索引来访问、操作集合中元素的方法,这使得它在需要按特定顺序维护元素序列的场景中得到了广泛应用。
List 接口的核心特性
1、有序性(Ordered):`List`集合中的元素存储和取出的顺序是一致的。你以什么样的顺序添加元素,遍历时就会得到什么样的顺序。这种有序性是通过索引来保证的,每个元素都占据一个特定的位置。
2、可重复性(Allows Duplicates):`List`允许添加重复的元素。两个元素`e1`和`e2`在`List`中可以是相等的(即 `e1.equals(e2)` 为 `true`),它们可以同时存在于集合的不同位置。
3、索引访问(Indexed):`List`中的每个元素都有一个唯一的、从0开始的整数索引。通过这个索引,可以精确地定位、访问和修改集合中的任何元素。这是`List`接口相较于`Collection`接口扩展的最重要功能之一。
常用实现类:ArrayList 与 LinkedList
立即学习“Java免费学习笔记(深入)”;
1、ArrayList:它的底层数据结构是动态数组(Dynamic Array)。因为是基于数组实现的,它在进行随机访问(通过索引`get(int index)`)时表现出极高的效率,时间复杂度为O(1)。当存储空间不足时,`ArrayList`会自动进行扩容,通常是创建一个更大的新数组并将旧数组的元素复制过去。但是在数组中间或开头位置插入、删除元素时,需要移动后续的大量元素,导致效率较低,时间复杂度为O(n)。
2、LinkedList:它的底层数据结构是双向链表(Doubly-linked List)。链表中的每个节点都存储了元素本身以及指向前一个和后一个节点的引用。这种结构使得它在插入和删除元素时具有天然的优势,特别是在列表的头部和尾部进行操作,只需要修改目标元素前后节点的指针即可,时间复杂度为O(1)。但是,在进行随机访问时,它需要从头节点或尾节点开始逐个遍历,直到找到指定索引的元素,因此查询效率较低,时间复杂度为O(n)。
List 集合常用方法详解
1、`void add(int index, E element)`:在列表的指定位置插入指定元素。此操作会将当前位于该位置的元素(若有)及任何后续元素向右移动一位,这在`ArrayList`中可能涉及数组复制,成本较高。
2、`E get(int index)`:返回列表中指定位置的元素。这是基于索引访问的核心方法,是`List`有序性的直接体现。
3、`E set(int index, E element)`:用指定元素替换列表中指定位置的元素,并返回被替换的旧元素。这个操作不会改变列表的大小。
4、`E remove(int index)`:移除列表中指定位置的元素,并返回被移除的元素。后续元素会向左移动以填补空缺。同样,在`ArrayList`中此操作可能引起元素的大规模移动。
5、`int indexOf(Object o)`:返回此列表中第一次出现的指定元素的索引,如果此列表不包含该元素,则返回-1。相对应地,`lastIndexOf(Object o)`方法用于查找最后一次出现的索引。
6、`List
遍历 List 集合的方式
1、使用传统的for循环与`get()`方法:通过一个计数器从0递增到`list.size() – 1`,在循环体内部使用`list.get(i)`来获取元素。这种方式对于`ArrayList`来说效率很高,但对于`LinkedList`则效率低下,因为它每次调用`get(i)`都需要从头开始遍历。
2、使用增强for循环(For-Each Loop):这是Java 5引入的语法糖,写法为 `for (Element e : list)`,代码简洁易读。编译器在底层会将其转换为迭代器(Iterator)的方式,因此它对于`ArrayList`和`LinkedList`都具有良好的性能,是推荐的遍历方式之一。
3、使用迭代器(Iterator):调用`list.iterator()`方法获取一个迭代器对象。通过`iterator.hasNext()`判断是否有下一个元素,通过`iterator.next()`获取下一个元素。使用迭代器的好处在于,可以在遍历过程中安全地删除元素(通过调用`iterator.remove()`方法),这种方式可以避免在遍历时直接修改集合而导致的`ConcurrentModificationException`异常。