List使用需要注意的问题
在Java中,list是我们常用的数据结构,一般我们都知道list根据底层数据结构的不同分为两种类型,分别是数组对应的ArrayList和链表对应的LinkedList,其特性从底层数据结构的不同可以很容易的被我们联想到,大致来说可以分为以下几个方向
- 插入和删除操作的效率:ArrayList在末尾进行插入和删除操作时效率较高,因为它可以直接通过下标访问元素。但是在中间或开头进行插入和删除操作时,需要移动后续元素,效率较低。而LinkedList在任意位置进行插入和删除操作时效率较高,因为只需要改变前后节点的指针即可。
- 随机访问的效率:ArrayList可以通过下标直接访问元素,因此在随机访问时效率较高。而LinkedList需要从头或尾开始遍历链表,直到找到目标位置,因此在随机访问时效率较低。
- 内存占用:ArrayList每个元素需要额外的空间存储下标信息,因此在存储大量元素时占用的内存更大。而LinkedList每个元素需要额外的空间存储前后节点的指针,因此在存储大量元素时占用的内存较小。
但是很多人不知道在Java中使用list集合还有很多需要注意的坑,今天先在这里记录几点
-
我们常说的list是指
java.util.List
包下的,而在java.util.Arrays
包下也有一个ArrayList,常见于Arrays.asList
方法返回,也就是说我们常用的数组转list的方法返回的是java.util.Arrays
包下的list,这是Arrays类里面的一个静态内部类,它和我们常用的ArrayList一样继承了AbstractList
,使用上需要注意的是Arrays的静态内部类是没有提供add、remove方法的,所以如果我们利用上述方法进行数组转list的话只能执行get操作 -
list.subList方法的使用。此方法返回的是原始list的一个子列表视图,本质上是AbstractList的一个内部类,其依然保存有原始列表的结构,如果对这个视图进行修改的话也会影响到原始列表,并且其add方法是会将数据加到最源头的list上,看到这里敏感的朋友应该能意识到可能会出现什么问题了
- 如果这种指代关系太深的话,那么就有可能会出现StackOverflowError
- 当你拿到的这个list只有几个元素,但其实它所指向的parent列表可能会远远超出你的想象,由此导致内存泄漏
-
ArrayList和LinkedList在不同垃圾回收算法下的表现
- 标记-清除算法:标记-清除算法通常会遍历整个内存堆,标记所有活动对象,然后清除未被标记的对象。在这种情况下,ArrayList和LinkedList的表现可能相似,因为它们都只是对象的集合,垃圾回收器会以相似的方式处理它们。
- 复制算法:复制算法将内存堆分为两个区域,每次只使用其中一个区域。活动对象被复制到另一个区域,然后清除当前区域中的所有对象。在这种情况下,ArrayList和LinkedList的表现可能有所不同。由于ArrayList使用连续的内存块存储元素,复制大型ArrayList可能会更耗时,因为需要复制整个连续块。而LinkedList使用链表结构,每个节点可以被单独复制,因此在复制算法下,LinkedList可能更高效。
- 标记-整理算法:标记-整理算法将存活对象向一端移动,然后清除剩余部分。在这种情况下,ArrayList和LinkedList的表现可能相似,因为它们都只是对象的集合,垃圾回收器会以相似的方式处理它们。