List实现方法及实现原理

本文详细介绍了ArrayList和LinkedList两种List接口实现类的实现原理和操作方法,包括自动变长机制、扩容策略以及不同操作的性能特点。ArrayList适合读取操作,插入删除效率低,而LinkedList在插入删除上具有优势,但在索引查找上效率低于ArrayList。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

与Collection接口相比,List接口新增了如下方法:

  • add(int index,E element):在列表指定位置插入指定元素
  • addAll(int index,Collection<? extends E> c):在指定位置添加集合元素
  • get(int index):获取指定位置的元素
  • indexOf(Object o):获取元素的第一个位置
  • isEmpty():判断列表中是否有元素
  • lastIndexOf(Object o):获取指定元素的最后一个位置
  • listIterator():获取列表元素的列表迭代器
  • listIterator(int index):从指定位置开始获取列表迭代器
  • remove(int index):移除指定位置的元素
  • set(int index,E element):用指定元素替换指定位置的元素
  • subList(int fromIdex,int toIndex):获取fromIndex到toIndex的元素列表

List接口实现类 有ArrayList、LinkedList、Vector和抽象类AbstractList,其中ArrayList和LinkedList较为常用

ArrayList:

自动变长机制
当调用add方法时,会调用ensureCapacityInternal方法进行扩容,每次扩容为原来大小的1.5倍,但是当第一次添加元素或者列表中元素个数小于10的话,列表容量默认为10,扩容之后,将指定元素复制到新的element数组中

扩容原理
根据当前数组的大小,判断是否小于默认值10,如果大于,则需要扩容至当前数组大小的1.5倍,重新将新扩容的数组数据copy只当前elementData,最后将传入的元素赋值给size++位置

//1.继承AbstractList和实现List
//2.底层是一个变长的数组,基本上等同于Vector
//3.对writeObject()和readObject()方法实现了同步
//4.是线程不安全的,多线程环境下要通过外部的同步策略后使用
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
            
        }

优点
操作读取操作效率高,基于数组实现的,可以为null值,可以允许重复元素,有序,异步。

缺点
由于它是由动态数组实现的,不适合频繁的对元素的插入和删除操作,因为每次插入和删除都需要移动数组中的元素。

增加方法:

  • colone():返回ArrayList实例的浅表副本
  • ensureCapacity(int minCapacity):增加ArrayList实例的容量。
    (改变list的容量会降低效率,改变list容量的原理是常见一个比之前更大容量的list,将原list中的数据copy到新的list,在删除原有的list)
  • removeRange(int fromIndex,int toIndex):批量移除列表中的元素
  • trimToSize():将列表容量调整至当前大小。

部分方法原理:

add(E e)
当调用add插入时,首先要调用ensureCapacityInternal(size + 1)方法,上文提到这个方法是进行自动扩容的,效率低重点也就是在这个扩容上了,每次新增都要对现有的数组进行一次1.5倍的扩大,数组间值的copy等,最后等扩容完毕,有空间位置了,将数组size+1的位置放入元素e,实现新增。

add(int index, E element)
给指定的index赋值element,首先rangeCheckForAdd(index)要对加入的index的位置进行check,index非法则抛出越界异常,throw new IndexOutOfBoundsException(outOfBoundsMsg(index));其他方法也有用到如:addAll(int index, Collection<? extends E> c),然后同理也是需要扩容ensureCapacityInternal(size + 1),完成扩容后,将数组间的数据进行一个copy,这主要是为了保证数组数据完整,空出一个index位置,不像add中的elementData[size++]就完事了,最后将指定位置的index元素赋值,个数+1。

remove(int index)
在删除index位置的元素时,要先调用rangeCheck(index)进行index的check,index要超过当前个数,则判定越界,抛出异常,throw new IndexOutOfBoundsException(outOfBoundsMsg(index));其他方法也有用到如:get(int index),set(int index, E element)等后面删除重点在于计算删除的index是末尾还是中间位置,末尾直接–,然后置空完事,如果是中间位置,那就要进行一个数组间的copy,重新组合数组数据了,性能就在这里得到了消耗。

get(int index)
获取指定index的元素,首先调用rangeCheck(index)进行index的check,通过后直接获取数组的下标index获取数据,就这么简单明了,没有任何多余操作,效率之高,相对LinkedList的遍历获取,较为方便高效。

LinkedList:

索引查找get(x)
所有根据索引的查找操作都是按照双向链表的需要执行的,根据索引从前或从后开始搜索,并且从最靠近索引的一端开始。例如一个LindedList有5个元素,如果调用了get(2)方法,LinkedList将会从头开始搜索;如果调用get(4)方法,那么LinkedList将会从后向前搜索。这样做的目的可以提升查找效率,因为在LinkedList内部有一个Node(int index)方法,它会判断从头或者从后开始查找比较快。

内部结构-链表Node
LinkedList的每个节点都是一个Node类型的实例,每个Node中都包含着当前元素,当前元素的前节点以及后节点,这样就实现了节点之间的环环相扣,跟链条一样的具备双向的链表结构。

//是一个双向链表
//实现了writeObject方法和readObject方法的同步
//线程不安全的,多线程环境下要通过外部的同步策略后使用
//可以存放任何元素,包括null
public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable{}

优点
LinkedList由双链表实现,增删由于不需要移动底层数组数据,其底层是链表实现的,只需要修改链表节点指针,对元素的插入和删除效率较高。

缺点
遍历效率较低。HashMap和双链表也有关系。

新增方法:

其中有部分方法实现的功能是一样的,例如add方法和offer方法实现的功能是一样的,只是返回的类型不同,add无返回类型,offer返回boolean。remove和poll方法的不同之处是:当列表为空时,poll返回null,而remove会抛出异常。

  • addFirst(E e):将指定元素添加到刘表开头
  • addLast(E e):将指定元素添加到列表末尾
  • descendingIterator():以逆向顺序返回列表的迭代器
  • element():获取但不移除列表的第一个元素
  • getFirst():返回列表的第一个元素
  • getLast():返回列表的最后一个元素
  • offerFirst(E e):在列表开头插入指定元素
  • offerLast(E e):在列表尾部插入指定元素
  • peekFirst():获取但不移除列表的第一个元素
  • peekLast():获取但不移除列表的最后一个元素
  • pollFirst():获取并移除列表的最后一个元素
  • pollLast():获取并移除列表的最后一个元素
  • pop():从列表所表示的堆栈弹出一个元素
  • push(E e);将元素推入列表表示的堆栈
  • removeFirst():移除并返回列表的第一个元素
  • removeLast():移除并返回列表的最后一个元素
  • removeFirstOccurrence(E e):从列表中移除第一次出现的指定元素
  • removeLastOccurrence(E e):从列表中移除最后一次出现的指定元素

部分方法原理:

add(E e)
将元素append至list的末尾,具体过程是:新建一个Node节点,其中将后面的那个节点last作为新节点的前置节点,后节点为null;将这个新Node节点作为整个list的后节点,如果之前的后节点l为null,将新建的Node作为list的前节点,否则,list的后节点指针指向新建Node,最后size+1,当前llist操作数modCount+1。在add一个新元素时,LinkedList所关心的重要数据,一共两个变量,一个first,一个last,这大大提升了插入时的效率,且默认是追加至末尾,保证了顺序,当然指定位置插入除外。

add(int index, E element)

remove(int index)
删除指定index的元素,删除之前要调用checkElementIndex(index)去check一下index是否存在元素,如果不存在抛出throw new IndexOutOfBoundsException(outOfBoundsMsg(index));越界错误,同样这个check方法也是很多方法用到的,如:get(int index),set(int index, E element)等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值