马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
在 Java 中,List 是一个非常常用的接口,提供了有序、可重复的元素聚集。List 接口有多个实现类,每个实现类都有其特定的特性和适用场景。以下是 Java 中重要实现了 List 接口的类及其详细先容。
1. 常见的 List 实现类
1.1 ArrayList
- 简介:
- 基于动态数组实现。
- 提供快速的随机访问(get 和 set 利用时间复杂度为 O(1))。
- 适合频仍的读取利用,但在插入和删除时可能较慢,尤其是在列表中间位置。
- 特点:
- 动态扩容:当数组容量不足时,会自动扩展(通常是当前容量的 1.5 倍)。
- 允许 null 元素。
- 非同步:不适合多线程环境下的并发访问,除非外部同步。
- 示例:
- import java.util.ArrayList;
- import java.util.List;
- public class ArrayListExample {
- public static void main(String[] args) {
- List<String> arrayList = new ArrayList<>();
- arrayList.add("Apple");
- arrayList.add("Banana");
- arrayList.add("Cherry");
- arrayList.add("Apple"); // 允许重复元素
- System.out.println("ArrayList: " + arrayList);
- }
- }
复制代码 输出:
- ArrayList: [Apple, Banana, Cherry, Apple]
复制代码 1.2 LinkedList
- 简介:
- 基于双向链表实现。
- 适合频仍的插入和删除利用,特殊是在列表的开头和中间位置。
- 支持作为队列(FIFO)和双端队列(Deque)的实现。
- 特点:
- 插入和删除效率高:在已知位置的插入和删除利用时间复杂度为 O(1)。
- 随机访问较慢:get 和 set 利用时间复杂度为 O(n),由于需要遍历链表。
- 允许 null 元素。
- 示例:
- import java.util.LinkedList;
- import java.util.List;
- public class LinkedListExample {
- public static void main(String[] args) {
- List<String> linkedList = new LinkedList<>();
- linkedList.add("Apple");
- linkedList.add("Banana");
- linkedList.add("Cherry");
- linkedList.add("Apple"); // 允许重复元素
- System.out.println("LinkedList: " + linkedList);
- }
- }
复制代码 输出:
- LinkedList: [Apple, Banana, Cherry, Apple]
复制代码 1.3 Vector
- 简介:
- 类似于 ArrayList,也是基于动态数组实现。
- 是同步的,线程安全的。
- 适合需要线程安全的场景,但由于同步开销,性能略低于 ArrayList。
- 特点:
- 同步:所有的公开方法都是同步的,适用于多线程环境。
- 动态扩容:默认扩容战略与 ArrayList 略有差别(通常是当前容量的两倍)。
- 允许 null 元素。
- 示例:
- import java.util.List;
- import java.util.Vector;
- public class VectorExample {
- public static void main(String[] args) {
- List<String> vector = new Vector<>();
- vector.add("Apple");
- vector.add("Banana");
- vector.add("Cherry");
- vector.add("Apple"); // 允许重复元素
- System.out.println("Vector: " + vector);
- }
- }
复制代码 输出:
- Vector: [Apple, Banana, Cherry, Apple]
复制代码 1.4 Stack
- 简介:
- 继承自 Vector,表现后进先出(LIFO)的堆栈。
- 提供了额外的方法,如 push、pop 和 peek。
- 特点:
- 继承自 Vector:具备线程安全性。
- LIFO 结构:适用于需要堆栈举动的场景。
- 已过时:由于继承自 Vector,不推荐使用。Java 推荐使用 Deque 接口及其实现类(如 ArrayDeque)来实现堆栈。
- 示例:
- import java.util.Stack;
- public class StackExample {
- public static void main(String[] args) {
- Stack<String> stack = new Stack<>();
- stack.push("Apple");
- stack.push("Banana");
- stack.push("Cherry");
- System.out.println("Stack: " + stack);
- String top = stack.pop();
- System.out.println("Popped element: " + top);
- System.out.println("Stack after pop: " + stack);
- }
- }
复制代码 输出:
- Stack: [Apple, Banana, Cherry]
- Popped element: Cherry
- Stack after pop: [Apple, Banana]
复制代码 1.5 CopyOnWriteArrayList
- 简介:
- 线程安全的 List 实现,适用于读多写少的并发场景。
- 基于“写时复制”机制,每次修改利用都会创建一个新的数组副本。
- 特点:
- 线程安全:无需外部同步。
- 高效的迭代:迭代时不会抛出 ConcurrentModificationException,适归并发读取。
- 写利用开销大:每次写利用都会复制整个数组,适合读多写少的场景。
- 示例:
- import java.util.List;
- import java.util.concurrent.CopyOnWriteArrayList;
- public class CopyOnWriteArrayListExample {
- public static void main(String[] args) {
- List<String> cowList = new CopyOnWriteArrayList<>();
- cowList.add("Apple");
- cowList.add("Banana");
- cowList.add("Cherry");
- System.out.println("CopyOnWriteArrayList: " + cowList);
- }
- }
复制代码 输出:
- CopyOnWriteArrayList: [Apple, Banana, Cherry]
复制代码 1.6 Stack 和 Deque 的对比
尽管 Stack 是 List 的一个实现类,但 Java 推荐使用 Deque 接口及其实现类(如 ArrayDeque)来替代 Stack,由于它们提供了更全面和灵活的堆栈和队列利用。
- 示例(使用 ArrayDeque 作为堆栈):
- import java.util.ArrayDeque;
- import java.util.Deque;
- public class ArrayDequeExample {
- public static void main(String[] args) {
- Deque<String> stack = new ArrayDeque<>();
- stack.push("Apple");
- stack.push("Banana");
- stack.push("Cherry");
- System.out.println("ArrayDeque as Stack: " + stack);
- String top = stack.pop();
- System.out.println("Popped element: " + top);
- System.out.println("Stack after pop: " + stack);
- }
- }
复制代码 输出:
- ArrayDeque as Stack: [Cherry, Banana, Apple]
- Popped element: Cherry
- Stack after pop: [Banana, Apple]
复制代码 2. List 实现类的选择依据
选择符合的 List 实现类取决于具体的使用场景和性能需求。以下是一些常见的选择依据:
- 需要快速的随机访问:
- 选择:ArrayList
- 理由:ArrayList 基于动态数组,实现了 O(1) 时间复杂度的随机访问。
- 需要频仍的插入和删除利用,特殊是在列表中间:
- 选择:LinkedList
- 理由:LinkedList 基于双向链表,在已知位置的插入和删除利用时间复杂度为 O(1)。
- 需要线程安全的列表:
- 选择:Vector 或 CopyOnWriteArrayList
- 理由:
- Vector:适用于需要同步访问且写利用较多的场景。
- CopyOnWriteArrayList:适用于读多写少的并发场景。
- 需要作为堆栈或队列使用:
- 选择:Deque 的实现类(如 ArrayDeque)
- 理由:Deque 提供了比 Stack 更灵活和高效的堆栈和队列利用。
3. List 实现类的性能对比
3.1 ArrayList 与 LinkedList
特性ArrayListLinkedList随机访问快速(O(1))较慢(O(n))插入和删除末尾插入和删除较快(O(1)),中间位置较慢(O(n))快速(O(1))内存使用较低(仅存储元素)较高(存储元素及前后节点的引用)迭代性能良好良好适用场景需要频仍随机访问的场景需要频仍插入和删除的场景 3.2 Vector 与 CopyOnWriteArrayList
特性VectorCopyOnWriteArrayList同步性方法级别同步基于写时复制的同步机制性能较低(由于同步开销)适用于读多写少的场景迭代安全性在迭代期间修改会抛出 ConcurrentModificationException迭代期间修改不会影响当前迭代适用场景需要线程安全且写利用频仍的场景需要线程安全且读利用频仍的场景 4. List 接口的常用方法
以下是 List 接口的一些常用方法及其说明:
方法描述add(E e)在列表末尾添加一个元素。add(int index, E element)在指定位置插入一个元素。get(int index)返回指定位置的元素。set(int index, E element)替换指定位置的元素。remove(int index)移除指定位置的元素。remove(Object o)移除第一个匹配的元素。size()返回列表中的元素个数。isEmpty()查抄列表是否为空。contains(Object o)查抄列表是否包含指定元素。clear()移除列表中的所有元素。indexOf(Object o)返回顾次出现指定元素的索引,如果不存在则返回 -1。lastIndexOf(Object o)返回最后一次出现指定元素的索引,如果不存在则返回 -1。subList(int fromIndex, int toIndex)返回列表的一个子视图,包含从 fromIndex(包含)到 toIndex(不包含)的元素。 4.1 示例:List 的常用利用
- import java.util.List;
- import java.util.ArrayList;
- public class ListMethodsExample {
- public static void main(String[] args) {
- List<String> fruits = new ArrayList<>();
- // 添加元素
- fruits.add("Apple");
- fruits.add("Banana");
- fruits.add("Cherry");
- fruits.add("Date");
- fruits.add("Elderberry");
- System.out.println("Initial List: " + fruits);
- // 插入元素
- fruits.add(2, "Coconut");
- System.out.println("After inserting Coconut at index 2: " + fruits);
- // 获取元素
- String fruitAt3 = fruits.get(3);
- System.out.println("Element at index 3: " + fruitAt3);
- // 修改元素
- fruits.set(1, "Blueberry");
- System.out.println("After setting index 1 to Blueberry: " + fruits);
- // 删除元素(按索引)
- fruits.remove(4);
- System.out.println("After removing element at index 4: " + fruits);
- // 删除元素(按对象)
- fruits.remove("Date");
- System.out.println("After removing 'Date': " + fruits);
- // 查询操作
- System.out.println("List size: " + fruits.size());
- System.out.println("List contains 'Apple': " + fruits.contains("Apple"));
- System.out.println("List contains 'Date': " + fruits.contains("Date"));
- // 获取子列表
- List<String> subFruits = fruits.subList(1, 3);
- System.out.println("Sublist from index 1 to 3: " + subFruits);
- // 清空列表
- fruits.clear();
- System.out.println("After clearing, is list empty? " + fruits.isEmpty());
- }
- }
复制代码 输出:
- Initial List: [Apple, Banana, Cherry, Date, Elderberry]
- After inserting Coconut at index 2: [Apple, Banana, Coconut, Cherry, Date, Elderberry]
- Element at index 3: Cherry
- After setting index 1 to Blueberry: [Apple, Blueberry, Coconut, Cherry, Date, Elderberry]
- After removing element at index 4: [Apple, Blueberry, Coconut, Cherry, Elderberry]
- After removing 'Date': [Apple, Blueberry, Coconut, Cherry, Elderberry]
- List size: 5
- List contains 'Apple': true
- List contains 'Date': false
- Sublist from index 1 to 3: [Blueberry, Coconut]
- After clearing, is list empty? true
复制代码 5. List 与其他聚集的对比
5.1 List vs Set
特性ListSet元素重复性允许重复元素不允许重复元素元素顺序保持元素的插入顺序,并支持按索引访问一般不保证元素的顺序(某些实现如 LinkedHashSet 保持插入顺序,TreeSet 按自然顺序或自界说顺序排序)实现类ArrayList、LinkedList、Vector 等HashSet、LinkedHashSet、TreeSet 等适用场景需要有序且可重复的元素聚集需要确保元素唯一性且不关心顺序(或有特定顺序要求) 5.2 List vs Queue
特性ListQueue元素访问方式支持按索引访问元素重要支持FIFO(先进先出)或其他特定的访问顺序常用实现类ArrayList、LinkedListLinkedList、PriorityQueue、ArrayDeque适用场景需要按恣意顺序访问和利用元素需要按特定顺序处置惩罚元素,如使命调理、消息队列等 6. 线程安全的 List 实现
在多线程环境下,选择符合的 List 实现类和同步机制非常重要,以确保数据的划一性和克制并发问题。
6.1 使用 Collections.synchronizedList
通过 Collections.synchronizedList 方法可以将任何 List 实现类包装为线程安全的列表。
- 示例:
- import java.util.List;
- import java.util.ArrayList;
- import java.util.Collections;
- public class SynchronizedListExample {
- public static void main(String[] args) {
- List<String> syncList = Collections.synchronizedList(new ArrayList<>());
- syncList.add("Apple");
- syncList.add("Banana");
- syncList.add("Cherry");
- // 迭代时需要手动同步
- synchronized (syncList) {
- for (String fruit : syncList) {
- System.out.println(fruit);
- }
- }
- }
- }
复制代码 6.2 使用 CopyOnWriteArrayList
CopyOnWriteArrayList 是一个线程安全的 List 实现,适用于读多写少的并发场景。
- 示例:
- import java.util.List;
- import java.util.concurrent.CopyOnWriteArrayList;
- public class CopyOnWriteArrayListThreadSafeExample {
- public static void main(String[] args) {
- List<String> cowList = new CopyOnWriteArrayList<>();
- cowList.add("Apple");
- cowList.add("Banana");
- cowList.add("Cherry");
- // 迭代时无需额外同步
- for (String fruit : cowList) {
- System.out.println(fruit);
- }
- }
- }
复制代码 6.3 性能考虑
- Collections.synchronizedList:
- 长处:适用于需要全面同步的场景。
- 缺点:需要手动同步迭代利用,可能导致性能瓶颈。
- CopyOnWriteArrayList:
- 长处:读利用无需同步,迭代时不会抛出 ConcurrentModificationException。
- 缺点:写利用开销较大,每次写利用都会复制整个数组。
7. List 的泛型与范例安全
List 接口是泛型接口,可以指定存储的元素范例,从而在编译时提供范例安全,克制范例转换错误。
7.1 使用泛型的优势
- 范例安全:在编译时查抄元素范例,克制运行时范例错误。
- 无需范例转换:获取元素时无需举行显式的范例转换。
- 示例:
- import java.util.List;
- import java.util.ArrayList;
- public class GenericListExample {
- public static void main(String[] args) {
- List<String> stringList = new ArrayList<>();
- stringList.add("Hello");
- stringList.add("World");
- // stringList.add(123); // 编译错误
- for (String str : stringList) {
- System.out.println(str.toUpperCase());
- }
- }
- }
复制代码 7.2 未使用泛型的风险
如果不使用泛型,List 将存储 Object 范例,可能导致运行时范例错误,需要举行范例转换。
- 示例:
- import java.util.List;
- import java.util.ArrayList;
- public class RawListExample {
- public static void main(String[] args) {
- List list = new ArrayList();
- list.add("Hello");
- list.add(123); // 允许添加不同类型的元素
- for (Object obj : list) {
- String str = (String) obj; // 可能抛出 ClassCastException
- System.out.println(str.toUpperCase());
- }
- }
- }
复制代码 输出:
- HELLO
- Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
- at RawListExample.main(RawListExample.java:10)
复制代码 8. 综合示例:List 的各种利用
以下是一个综合示例,展示了 List 的常用利用,包罗添加、插入、删除、修改、查询和遍历等。
- import java.util.List;
- import java.util.ArrayList;
- import java.util.Iterator;
- public class ComprehensiveListExample {
- public static void main(String[] args) {
- // 创建一个 ArrayList
- List<String> fruits = new ArrayList<>();
-
- // 添加元素
- fruits.add("Apple");
- fruits.add("Banana");
- fruits.add("Cherry");
- fruits.add("Date");
- fruits.add("Elderberry");
- System.out.println("Initial List: " + fruits);
-
- // 插入元素
- fruits.add(2, "Coconut");
- System.out.println("After inserting Coconut at index 2: " + fruits);
-
- // 获取元素
- String fruitAt3 = fruits.get(3);
- System.out.println("Element at index 3: " + fruitAt3);
-
- // 修改元素
- fruits.set(1, "Blueberry");
- System.out.println("After setting index 1 to Blueberry: " + fruits);
-
- // 删除元素(按索引)
- fruits.remove(4);
- System.out.println("After removing element at index 4: " + fruits);
-
- // 删除元素(按对象)
- fruits.remove("Date");
- System.out.println("After removing 'Date': " + fruits);
-
- // 查询操作
- System.out.println("List size: " + fruits.size());
- System.out.println("List contains 'Apple': " + fruits.contains("Apple"));
- System.out.println("List contains 'Date': " + fruits.contains("Date"));
-
- // 获取子列表
- List<String> subFruits = fruits.subList(1, 3);
- System.out.println("Sublist from index 1 to 3: " + subFruits);
-
- // 遍历列表(使用增强的 for 循环)
- System.out.println("Iterating using enhanced for loop:");
- for (String fruit : fruits) {
- System.out.println(fruit);
- }
-
- // 遍历列表(使用 Iterator)
- System.out.println("Iterating using Iterator:");
- Iterator<String> iterator = fruits.iterator();
- while (iterator.hasNext()) {
- System.out.println(iterator.next());
- }
-
- // 使用 Lambda 表达式和 forEach 方法遍历
- System.out.println("Iterating using forEach and Lambda:");
- fruits.forEach(fruit -> System.out.println(fruit));
-
- // 清空列表
- fruits.clear();
- System.out.println("After clearing, is list empty? " + fruits.isEmpty());
- }
- }
复制代码 输出:
- Initial List: [Apple, Banana, Cherry, Date, Elderberry]
- After inserting Coconut at index 2: [Apple, Banana, Coconut, Cherry, Date, Elderberry]
- Element at index 3: Cherry
- After setting index 1 to Blueberry: [Apple, Blueberry, Coconut, Cherry, Date, Elderberry]
- After removing element at index 4: [Apple, Blueberry, Coconut, Cherry, Elderberry]
- After removing 'Date': [Apple, Blueberry, Coconut, Cherry, Elderberry]
- List size: 5
- List contains 'Apple': true
- List contains 'Date': false
- Sublist from index 1 to 3: [Blueberry, Coconut]
- Iterating using enhanced for loop:
- Apple
- Blueberry
- Coconut
- Cherry
- Elderberry
- Iterating using Iterator:
- Apple
- Blueberry
- Coconut
- Cherry
- Elderberry
- Iterating using forEach and Lambda:
- Apple
- Blueberry
- Coconut
- Cherry
- Elderberry
- After clearing, is list empty? true
复制代码 9. 性能优化与最佳实践
9.1 预设容量
在已知大致元素数目时,可以在创建 ArrayList 时预设初始容量,以淘汰扩容次数,提拔性能。
- 示例:
- List<String> largeList = new ArrayList<>(1000); // 预设初始容量为 1000
复制代码 9.2 使用接口范例声明
在声明变量时,使用接口范例(如 List)而不是具体实现类(如 ArrayList),提高代码的灵活性和可维护性。
- 示例:
- List<String> list = new ArrayList<>();
- // 未来可以轻松切换为其他实现类,如 LinkedList
- list = new LinkedList<>();
复制代码 9.3 克制频仍的自动装箱与拆箱
对于存储根本数据范例的 List,使用其包装类(如 Integer、Double)时,应留意克制频仍的自动装箱与拆箱,以提拔性能。
- 示例:
- List<Integer> numbers = new ArrayList<>();
- for (int i = 0; i < 1000; i++) {
- numbers.add(i); // 自动装箱
- }
复制代码 优化:如果需要大量的数值数据处置惩罚,考虑使用 IntStream 或其他专用的数据结构。
10. 总结
List 接口在 Java 聚集框架中占据重要职位,提供了有序、可重复的元素聚集。通过了解差别 List 实现类的特性、性能和适用场景,可以在项目中选择最符合的实现类,从而优化代码的性能和可维护性。以下是关键要点:
- 选择符合的实现类:
- ArrayList:适用于需要快速随机访问和较少插入删除利用的场景。
- LinkedList:适用于需要频仍插入和删除利用,尤其是在列表中间位置的场景。
- Vector:适用于需要线程安全的场景,但由于同步开销,性能较低。
- CopyOnWriteArrayList:适用于读多写少的并发场景。
- 使用泛型:提高范例安全,克制运行时范例错误。
- 遵循接口编程:使用接口范例声明变量,增强代码的灵活性和可维护性。
- 性能优化:根据具体需求预设容量,克制不必要的装箱与拆箱。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |