List 接口的实现类

在 Java 中,List 是一个非常常用的接口,提供了有序、可重复的元素集合。List 接口有多个实现类,每个实现类都有其特定的特性和适用场景。以下是 Java 中主要实现了 List 接口的类及其详细介绍。

1. 常见的 List 实现类

1.1 ArrayList

  • 简介

    • 基于动态数组实现。
    • 提供快速的随机访问(getset 操作时间复杂度为 O(1))。
    • 适合频繁的读取操作,但在插入和删除时可能较慢,尤其是在列表中间位置。
  • 特点

    • 动态扩容:当数组容量不足时,会自动扩展(通常是当前容量的 1.5 倍)。
    • 允许 null 元素
    • 非同步:不适合多线程环境下的并发访问,除非外部同步。
  • 示例

    java 复制代码
    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)。
    • 随机访问较慢getset 操作时间复杂度为 O(n),因为需要遍历链表。
    • 允许 null 元素
  • 示例

    java 复制代码
    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 元素
  • 示例

    java 复制代码
    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)的堆栈。
    • 提供了额外的方法,如 pushpoppeek
  • 特点

    • 继承自 Vector:具备线程安全性。
    • LIFO 结构:适用于需要堆栈行为的场景。
    • 已过时 :由于继承自 Vector,不推荐使用。Java 推荐使用 Deque 接口及其实现类(如 ArrayDeque)来实现堆栈。
  • 示例

    java 复制代码
    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,适合并发读取。
    • 写操作开销大:每次写操作都会复制整个数组,适合读多写少的场景。
  • 示例

    java 复制代码
    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 StackDeque 的对比

尽管 StackList 的一个实现类,但 Java 推荐使用 Deque 接口及其实现类(如 ArrayDeque)来替代 Stack,因为它们提供了更全面和灵活的堆栈和队列操作。

  • 示例(使用 ArrayDeque 作为堆栈)

    java 复制代码
    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)。
  • 需要线程安全的列表

    • 选择VectorCopyOnWriteArrayList
    • 理由
      • Vector:适用于需要同步访问且写操作较多的场景。
      • CopyOnWriteArrayList:适用于读多写少的并发场景。
  • 需要作为堆栈或队列使用

    • 选择Deque 的实现类(如 ArrayDeque
    • 理由Deque 提供了比 Stack 更灵活和高效的堆栈和队列操作。

3. List 实现类的性能对比

3.1 ArrayListLinkedList

特性 ArrayList LinkedList
随机访问 快速(O(1)) 较慢(O(n))
插入和删除 末尾插入和删除较快(O(1)),中间位置较慢(O(n)) 快速(O(1))
内存使用 较低(仅存储元素) 较高(存储元素及前后节点的引用)
迭代性能 良好 良好
适用场景 需要频繁随机访问的场景 需要频繁插入和删除的场景

3.2 VectorCopyOnWriteArrayList

特性 Vector CopyOnWriteArrayList
同步性 方法级别同步 基于写时复制的同步机制
性能 较低(由于同步开销) 适用于读多写少的场景
迭代安全性 在迭代期间修改会抛出 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 的常用操作

java 复制代码
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

特性 List Set
元素重复性 允许重复元素 不允许重复元素
元素顺序 保持元素的插入顺序,并支持按索引访问 一般不保证元素的顺序(某些实现如 LinkedHashSet 保持插入顺序,TreeSet 按自然顺序或自定义顺序排序)
实现类 ArrayListLinkedListVector HashSetLinkedHashSetTreeSet
适用场景 需要有序且可重复的元素集合 需要确保元素唯一性且不关心顺序(或有特定顺序要求)

5.2 List vs Queue

特性 List Queue
元素访问方式 支持按索引访问元素 主要支持FIFO(先进先出)或其他特定的访问顺序
常用实现类 ArrayListLinkedList LinkedListPriorityQueueArrayDeque
适用场景 需要按任意顺序访问和操作元素 需要按特定顺序处理元素,如任务调度、消息队列等

6. 线程安全的 List 实现

在多线程环境下,选择合适的 List 实现类和同步机制非常重要,以确保数据的一致性和避免并发问题。

6.1 使用 Collections.synchronizedList

通过 Collections.synchronizedList 方法可以将任何 List 实现类包装为线程安全的列表。

  • 示例

    java 复制代码
    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 实现,适用于读多写少的并发场景。

  • 示例

    java 复制代码
    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 使用泛型的优势

  • 类型安全:在编译时检查元素类型,避免运行时类型错误。

  • 无需类型转换:获取元素时无需进行显式的类型转换。

  • 示例

    java 复制代码
    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 类型,可能导致运行时类型错误,需要进行类型转换。

  • 示例

    java 复制代码
    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 的常用操作,包括添加、插入、删除、修改、查询和遍历等。

java 复制代码
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 时预设初始容量,以减少扩容次数,提升性能。

  • 示例

    java 复制代码
    List<String> largeList = new ArrayList<>(1000); // 预设初始容量为 1000

9.2 使用接口类型声明

在声明变量时,使用接口类型(如 List)而不是具体实现类(如 ArrayList),提高代码的灵活性和可维护性。

  • 示例

    java 复制代码
    List<String> list = new ArrayList<>();
    // 未来可以轻松切换为其他实现类,如 LinkedList
    list = new LinkedList<>();

9.3 避免频繁的自动装箱与拆箱

对于存储基本数据类型的 List,使用其包装类(如 IntegerDouble)时,应注意避免频繁的自动装箱与拆箱,以提升性能。

  • 示例

    java 复制代码
    List<Integer> numbers = new ArrayList<>();
    for (int i = 0; i < 1000; i++) {
        numbers.add(i); // 自动装箱
    }

    优化 :如果需要大量的数值数据处理,考虑使用 IntStream 或其他专用的数据结构。

10. 总结

List 接口在 Java 集合框架中占据重要地位,提供了有序、可重复的元素集合。通过了解不同 List 实现类的特性、性能和适用场景,可以在项目中选择最合适的实现类,从而优化代码的性能和可维护性。以下是关键要点:

  • 选择合适的实现类

    • ArrayList:适用于需要快速随机访问和较少插入删除操作的场景。
    • LinkedList:适用于需要频繁插入和删除操作,尤其是在列表中间位置的场景。
    • Vector:适用于需要线程安全的场景,但由于同步开销,性能较低。
    • CopyOnWriteArrayList:适用于读多写少的并发场景。
  • 使用泛型:提高类型安全,避免运行时类型错误。

  • 遵循接口编程:使用接口类型声明变量,增强代码的灵活性和可维护性。

  • 性能优化:根据具体需求预设容量,避免不必要的装箱与拆箱。

相关推荐
m0_675988232 小时前
Leetcode2270:分割数组的方案数
数据结构·算法·leetcode·python3
涔溪3 小时前
JS二叉树是什么?二叉树的特性
java·javascript·数据结构
步、步、为营3 小时前
Web 实时消息推送的七种实现方案
windows·c#·list
2403_875180954 小时前
一键掌握多平台短视频矩阵营销/源码部署
java·前端·数据结构·线性代数·矩阵·php
.Vcoistnt5 小时前
Codeforces Round 976 (Div. 2) and Divide By Zero 9.0(A-E)
数据结构·c++·算法·贪心算法·动态规划·图论
大桔骑士v5 小时前
【数据结构学习笔记】19:跳表(Skip List)
数据结构·链表·跳表·skiplist
qingy_20466 小时前
【算法】图解排序算法之归并排序、快速排序、堆排序
java·数据结构·算法
shinelord明7 小时前
【再谈设计模式】模板方法模式 - 算法骨架的构建者
开发语言·数据结构·设计模式·软件工程
٩( 'ω' )و2607 小时前
链表的中间结点
数据结构·链表