在Java(JDK8)中,集合(Collection)是数据结构的实现,用于存储和操作对象集合。
集合(Collection)中包含的一般类或接口:
在这其中呢,我们经常使用的其实就是List、Set、Queue这三个接口及其实现类,那我们分别介绍一下这些接口/类的常用方法和使用中需要注意的地方:
1、List
List
是Java集合框架中的一个接口,它代表一个有序的集合(也称为序列)。List
接口中的元素允许重复,并且元素具有索引,即每个元素都有一个特定的位置,可以通过索引来访问。它定义了一组有序的、可重复的、可以包含null元素、并允许通过索引位置访问和操作其中的元素的集合。
1.1 使用场景
- 需要维护元素的插入顺序,即列表的顺序与其添加元素的顺序一致。
- 通常用于需要频繁进行增删改查操作,尤其是根据索引进行操作的情况。
- 需要支持按索引查找、迭代遍历或存储有序数据集的应用场景。
1.2 作用
- 提供了丰富的API来实现对列表元素的各种操作,如添加、删除、修改和查询等。
- 支持通过索引快速访问列表中特定位置的元素,时间复杂度为O(1)(对于ArrayList而言)。
- 允许重复元素,并且保持元素的插入顺序。
1.3 常用的实现类
ArrayList
:基于动态数组实现,适用于随机访问和遍历,插入和删除操作在非尾部时可能效率较低,因为涉及数组元素的移动。LinkedList
:基于双向链表实现,插入和删除操作在任何位置都较为高效,但随机访问的时间复杂度较高(O(n))。Vector
:类似于ArrayList,但是线程安全,但在多线程环境下性能较差,不推荐在新的代码中使用,可以考虑使用CopyOnWriteArrayList
替代以保证线程安全性。
1.3.1 Vector和ArrayList的区别:
Vector和ArrayList主要区别在于线程安全、性能和扩容策略方面。
从线程安全的角度来看,Vector是线程安全的,因为它的方法使用了synchronized关键字来实现同步,而ArrayList则不是线程安全的。这意味着在多线程环境下,使用Vector不需要额外的同步措施,而ArrayList则需要外部同步来保证数据的一致性。
在性能方面,由于ArrayList没有使用synchronized加锁,它的处理速度通常会更快。这是因为synchronized会带来一定的性能开销,尤其是在高并发的情况下。
两者在扩容策略上也有所不同。当需要增加容量时,Vector默认会将容量翻倍,而ArrayList则是增加50%的容量。这种差异影响了它们在面对大量数据插入时的性能表现。
在实际开发中多线程编程更倾向于使用
Collections.synchronizedList()
方法来包装一个ArrayList,或者使用CopyOnWriteArrayList
(适用于读多写少的场景)来替代Vector,这样可以更好地平衡线程安全性和性能。在实际开发中,直接使用Vector的情况相对较少。
1.3.2 ArrayList和LinkedList的区别:
ArrayList和LinkedList的主要区别在于它们的底层数据结构、随机访问效率以及插入和删除操作的效率。
从底层数据结构来看,ArrayList是基于动态数组实现的,而LinkedList则是基于双向链表实现的。这意味着ArrayList在内存中是连续存储的,而LinkedList则是通过节点和指针来存储元素。
在随机访问效率方面,ArrayList通常优于LinkedList。因为ArrayList可以直接通过数组下标快速定位到元素,而LinkedList需要从头或尾遍历节点直到找到目标元素,这在时间复杂度上是线性的。
在插入和删除操作的效率上,LinkedList通常比ArrayList更胜一筹。这是因为ArrayList在插入和删除时可能需要进行数组的扩容和元素的复制,而LinkedList只需要修改相应的指针即可完成操作,这在时间复杂度上更为高效。
两者在内存空间占用上也有所不同。ArrayList由于是连续存储,可能会有更大的内存开销,尤其是在列表规模较大时。而LinkedList由于需要额外的节点信息来存储指针,也会有一定的内存开销。
简单的说,ArrayList适合需要快速随机访问和较少插入删除操作的场景,而LinkedList适合需要频繁插入删除操作且对随机访问要求不高的场景。在实际开发中,应根据具体需求和性能考量来做出选择。
1.4 常用的方法
- 添加元素:
add(E element)
、add(int index, E element)、
addAll(
Collection<? extends E> c) - 删除元素:
remove(Object o)
、remove(int index)
- 修改元素:
set(int index, E element)
- 查询元素:
get(int index)
- 判断是否包含某个元素:
contains(Object o)
- 清空列表:
clear()
- 获取列表大小:
size()
- 排序:
sort(List<T> list)
(Java 8及以上版本,调用Collections.sort方法) - 翻转列表:
Collections.reverse(list)
- 查找索引:
indexOf(Object o)
、lastIndexOf(Object o)
- 子列表:
subList(int fromIndex, int toIndex)
1.5 使用时需要注意的问题:
- 线程安全性:
List
接口本身不是线程安全的,如果多个线程同时修改List
,可能会导致数据不一致。需要外部同步或使用线程安全的实现类(如Vector
或CopyOnWriteArrayList
)。 - 性能:不同的
List
实现类有不同的性能特点。例如,ArrayList
在随机访问时较快,但在中间位置插入和删除元素时较慢;而LinkedList
在插入和删除时较快,但随机访问较慢。根据使用场景选择合适的实现类。 - 索引越界:在使用
get
、set
或remove
方法时,如果提供的索引超出列表的范围(小于0或大于等于列表大小),将会抛出IndexOutOfBoundsException
异常。 - 元素类型:向
List
中添加的元素必须与声明的类型一致,否则在运行时可能会抛出ClassCastException
异常。 - 空指针异常:如果向
List
中添加null
元素,并且后续代码中没有对null
进行适当处理,可能会导致NullPointerException
异常。
具体的方法示例:后续跟进补充。。。