Java List接口及实现类
Java中的List接口是java.util包下集合框架的一部分,它是Collection接口的直接子接口。List接口定义了一个有序的、可变的元素序列,并且允许重复元素。它提供了对元素进行索引访问的能力,以及通过索引位置添加、删除和修改元素的方法。
以下是List接口的一些关键特性:
- 有序:列表中元素的顺序是明确的,可以通过索引(从0开始)来确定每个元素的位置。
- 可变大小:列表的长度可以动态地改变,允许在运行时添加或删除元素。
- 允许重复:与Set不同,List允许存储重复的对象。
- 索引操作:提供了get(int index)方法获取指定索引处的元素,set(int index, E element)方法替换指定索引处的元素,add(int index, E element)方法在特定位置插入元素等。
- 迭代器:除了继承自
Collection的iterator()方法外,还提供了listIterator()方法,返回一个ListIterator,它可以双向遍历列表并支持通过索引操作元素。
实现List接口的主要类有:
ArrayList- 基于动态数组实现,查询和随机访问速度快(O(1)时间复杂度),因为内部数据结构是一个数组,可以直接通过索引寻址。
- 插入和删除元素速度相对较慢,特别是当操作发生在中间位置时,因为可能需要移动后续元素以维持连续数组结构。
- 不是线程安全的,如果需要并发操作,需外部同步。
LinkedList- 基于双向链表实现,插入和删除元素(尤其是头尾操作)的速度较快(通常为O(1),但在列表中间操作的时间复杂度为O(n))。
- 随机访问速度较慢,因为查找元素需要沿着链表逐个节点查找。
- 由于其链表特性,也支持作为堆栈、队列或者双端队列使用,因为它实现了Deque接口。
- 同样不是线程安全的,如需并发访问,也需要外部同步控制。
Vector- 是早期JDK版本中的线程安全的List实现,与ArrayList类似基于动态数组。
- 所有方法都是同步的,因此在多线程环境下不需要额外的同步控制,但这也意味着性能开销较大。
- 在新的Java开发中,一般推荐使用
ArrayList配合Collections.synchronizedList()方法来获得线程安全的List,而不是直接使用Vector。
这些类的选择主要取决于应用的需求,例如是否需要高效地进行索引访问、频繁的插入/删除操作,还是需要线程安全的环境等。
其他实现线程安全的方法
Java并发工具包(JUC, Java Util Concurrency)中的CopyOnWriteArrayList类可以用来控制同步,但它实现的是另一种形式的线程安全策略,而不是传统的锁机制。
CopyOnWriteArrayList在并发环境下通过以下方式提供线程安全性:
写时复制(Copy-On-Write, COW)策略:
当有写操作(添加、删除或更新元素)发生时,它并不会直接修改原有的数据结构(数组)。相反,它会创建原始数组的一个副本,并在副本上执行修改操作。一旦修改完成,就将原引用指向新的数组。这意味着读操作不会被写操作阻塞,因为它们始终访问的是一个不变的数组版本。读取无锁:
由于写操作不影响已存在的数组实例,因此多个读取线程可以同时并发地遍历列表而无需加锁,从而提高了读密集型应用的性能。写入互斥:
当有多个写入请求时,CopyOnWriteArrayList内部会使用synchronized关键字来确保同一时间只有一个写入操作能够修改底层的数据结构。这保证了写入操作的原子性,但代价是在并发写入较多的情况下可能会有较高的内存开销和较慢的写入速度。
总结来说,虽然CopyOnWriteArrayList实现了线程安全,但它并不适合于频繁写入且需要实时看到最新写入值的场景。它的设计主要用于那些读多写少且能容忍一定程度数据延迟一致性的场景。