【Java源码解析第一篇】List

首先先来看一下继承结构图:
在这里插入图片描述
如图所示为List的继承关系结构图。由图可知,Java的Collection集合类采用了设计模式里的模板模式——接口定义方法,抽象类实现基础通用功能,具体实现类继承抽象类并进行功能的自定义实现。

Collection接口

Collection接口定义了Java集合类的通用方法,如元素的添加、删除等方法,其方法列表可见下图。由于接口只是方法定义,其重要之处在于设计理念,这里不做过多介绍。
在这里插入图片描述

List接口

List接口方法列表图如图所示,如图可知,List接口相对Collection接口而言多了许多方法,主要包含一些针对列表数据结构的方法重载以及元素的增加、删除、索引方法,以及一个特殊的双向迭代器ListIterator。
在这里插入图片描述

AbstractCollection

在这里插入图片描述
AbstractCollection对Collection接口的一些方法提供默认实现:

  • 对size()和iterator()保留抽象,由子类实现
  • 通过判断size()是否为0判断是否为empty
  • contains()的实现为获取迭代器对集合元素进行迭代查找
  • toArray()使用系统数组拷贝形式实现
  • add()默认抛出异常,这是由于不同的数据结构实现列表添加元素方式不同,且存在列表不支持结构性改变操作的需求
  • remove()使用迭代器遍历查找目标并删除目标
  • containsAll()遍历调用contains()
  • addAll()遍历调用add()
  • clear()遍历调用remove()

AbstractList

在这里插入图片描述
如图可知,AbstractList抽象类主要对List接口的一些基本功能进行实现,其保留了大部分继承自AbstractCollection的默认实现,仅对三个方法做了重写操作。

  • add()、set()、remove()全部抛出不支持异常,交给子类复写实现,若不复写则默认不支持结构性改变操作
  • get()仍然交给不同的实现类根据自己的数据结构自行实现
  • 对于indexOf()、clear()……等方法其实现原理均与AbstractCollection类似,不再复述
  • AbstractList返回的迭代器是内部类,对于迭代器的实现,主要依赖两个索引指针cursor和lastRet;前者是下一个元素的索引,后者是当前元素的索引,next()方法直接get(cursor)并更新lastRet=cursor和cursor++。ListIterator相比较Iterator具备双向移动的特点,在向前移动时实际上只是将cursor的初始值指向了末尾并做递减操作。
  • subList()根据随机访问属性创建内部类对象,该内部类List继承AbstractList。
class SubList<E> extends AbstractList<E> {
    private final AbstractList<E> l;
    private final int offset;
    private int size;

    SubList(AbstractList<E> list, int fromIndex, int toIndex) {
        if (fromIndex < 0)
            throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
        if (toIndex > list.size())
            throw new IndexOutOfBoundsException("toIndex = " + toIndex);
        if (fromIndex > toIndex)
            throw new IllegalArgumentException("fromIndex(" + fromIndex +
                                               ") > toIndex(" + toIndex + ")");
        l = list;
        offset = fromIndex;
        size = toIndex - fromIndex;
        this.modCount = l.modCount;
    }

如代码所示,l=list即两者指向的是同一个列表对象,因此原列表的改变同样会在子列表中表现出来,同时这个内部类的所有结构性改变方法都会进行结构性次数比对,当发现原列表的结构改变次数与此SubList对象改变次数不一致时,会抛出异常。这是由于结构性改变发生时会导致子列表的offset和size等发生异常。

  • modCount用来记录结构性改变次数,主要用来告知迭代器,如果发生意外结构性改变迭代器将抛出异常。

ArrayList

  • 底层是数组,使用elementData[]保存数据。
  • 内部提前定义好缓存数组elementData,根据构造时提供的初始化容量来新建一个容量大小的数组对象,如果不指定则指向一个默认容量的空数组。使用其他collection进行构造时,elementData直接指向传入的集合.toArray(),并将数组转型为object[]类型.
  • trimToSize()如果list存放的元素数size小于elementData的length,使用数组复制的方式将元素赋值到length为size大小的新数组中去。
  • 扩容操作均是通过数组复制方式将元素复制到更大容量的数组中
  • clone返回新对象
  • get读取数组,set覆盖元素返回旧值,add末尾添加或中间添加(数组复制),remove根据序号或者值进行删除,序号直接数组复制,值需要遍历查找再数组复制
  • clear使用for遍历置null(不会改变结构)
  • sort使用Arrays.sort
  • foreach() 支持lamed表达式的foreach遍历

AbstractSequentialList

继承自AbstractList,提供有序数据存储的基础实现

LinkedList

  • 底层为双向链表,Node数据结构如下:
E item;
Node<E> next;
Node<E> prev;
  • 属性记录首节点和尾节点。
  • linkFirst 新建一个前指向为空,后节点指向原首节点的新节点,然后将原节点的前指针指向这个新节点,如果原首节点为空,则新节点也标记为尾节点。其他link方法参考该思路
  • clear遍历置为null
  • 查找某个索引的node,根据索引决定从头还是从尾找,依次遍历赋值next直到抵达索引位置然后返回。
  • 删除节点时直接根据当前节点记录的前后节点指针获取前后节点,并将前后节点对当前节点的指针进行删除(置null)修正工作。
  • 对接口Deque实现的方法如poll、push等操作,本质也是对首尾节点的操作,不再复述。
  • get(index)会根据index距离首尾距离进行遍历,直到index,因此linkedlist用for循环遍历会带来极大的性能开销,应该使用迭代器。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值