先来说一下ArrayList和LinkedList的区别的,再梳理一遍,加强记忆和理解。
ArrayList底层使用数组实现,连续内存;LinkedList底层使用链表实现,不一定连续的内存。
ArrayList查找效率较高,增删效率低;LinkedList与之相反,增删效率高,查找效率低。
这是为什么呢?
先説查找操作,ArrayList使用下标、连续内存,而LinkedList不连续,一个个next遍历的时候,ArrayList效率肯定较高;
(ArrayList查找的时间复杂度为1,而LinkedList的时间复杂度为n)
再说增加操作,由于ArrayList底层使用连续的数组,在增加元素时,首先需要考虑扩充数组容量,再然后可能要移位;而LinkedList只需要再开辟一个节点的空间,改变链表指针指向的方向即可。
例如在一个集合中有A,B,C,D四个元素,再执行list.add(1, "E");语句时
ArrayList需要将D,C,B三个元素依次复制向右移位一位,然后再插入新元素
LinkedList只需要再开辟一个新的内存空间,改变链表指针指向的方向即可
删除操作类似。
LinkedList源码分析
在LinkedList中定义了三个全局变量,分别是int size,Node<E> first,Node<E> last
其中size是集合的大小,first是第一个节点,last是第二个节点。这也反应了LinkedList底层使用的是双向链表
first是查询操作的开始节点,last是添加操作的开始节点
接下来就很简单了,无非就是节点的first和last之间的相互赋值。
下面自己写一个代码,模拟LinkedList的实现过程
1、先定义节点信息
2、实现add方法
如果添加的元素是第一个,则链表的头节点是它,尾节点也是它;如果不是第一个了,则需要改变一下原来尾节点的指针
最后新加节点作为尾部,链表长度+1.
3、实现get方法
get之前首先必须要判断下标是否越界:大于等于0小于size(下标越界应抛出IndexOutOfBoundsException异常,此处省略)
然后最简单的方法就是,逐个遍历,到元素index,当然JDK中不会写效率这么低的方法,它们充分利用双线机制和收尾两个节点,进行二分查找。
getNode方法是获取对应下标的节点(包括节点数据和前后指针)
4、实现remove方法
首先还是要判断下标是否要越界:大于等于0小于size,然后获取到要删除的节点,改变其前后节点的指针指向。
注意:删除收尾节点时,还要修改first或last变量,最后size-1
注意红框中的程序中的步骤:
第一步:将节点B指向的下一节点改为D,原来指向C的那条线算是断了;
第二步:将待删除节点C指向的前一节点赋值为null,提醒垃圾回收机制进行回收;
第三步:将节点D指向的前一节点改为B,原来指向C的那条线算是断了;
第四步:将待删除节点C指向的下一节点赋值为null,提醒垃圾回收机制进行回收;
最后再将待删除节点的数据部分置为null,进行回收内存。
5、实现按下标add方法
首先还是要判断下标是否要越界:大于等于0小于等于size(此处index可以等于size,相当于在最后add)
在这里还是要注意一下在首尾插入的情况
综上几个方法,只要带下标的方法,都利用了双向链表机制进行二分查找,增加了效率。