我们先把LinkedList的增删改查源码都阅读一遍,然后再各个方面进行比较两者之间的区别。
LinkedList linkedList = new LinkedList();
// add
linkedList.add("1");
// get
linkedList.get(0);
// set
linkedList.set(0, "1");
// remove
linkedList.remove("1");
ArrayList是对象数组,LinkedList是链表
先从新增开始,他的步骤也十分的简单,和ArrayList完全不同的是,一看他的数据结构,属性中包含next多半是链表的结构,但是具体是什么链表,我们还要继续看他的其他方法。
/**
* Links e as last element.
*/
void linkLast(E e) {
// 最后一个元素赋值给l
final LinkedList.Node<E> l = last;
// 新建一个节点赋值给newNode
// 下一个节点为空
// item为设置的节点
// 最后一个节点赋值给上一个节点
final LinkedList.Node<E> newNode = new LinkedList.Node<>(l, e, null);
// 当前新建的节点赋值为最后一个节
last = newNode;
// 最后一个节点为空
// 也就是第一个节点被设置的时候
if (l == null)
// 设置最新的节点为第一个节点
first = newNode;
else
// 新增前的最后一个节点的next属性为当前新增节点
l.next = newNode;
// 长度自增
size++;
modCount++;
}
LinkedList是双向链表
我们再看看他内部的Node对象有哪些属性。
Node(LinkedList.Node<E> prev, E element, LinkedList.Node<E> next) {
// 当前节点
this.item = element;
// 下一个节点
this.next = next;
// 上一个节点
this.prev = prev;
}
然后继续看他的取数逻辑是怎样的,一会儿从前面第一个节点开始,一会儿从最后一个节点开始取数。
/**
* Returns the (non-null) Node at the specified element index.
*/
LinkedList.Node<E> node(int index) {
// assert isElementIndex(index);
// 下标 小于 长度除以2
if (index < (size >> 1)) {
// 第一个节点赋值给x
LinkedList.Node<E> x = first;
// 正向遍历到下标节点为止
for (int i = 0; i < index; i++)
// x的下一个元素赋值给x
x = x.next;
// 返回到下标为止的x对象
return x;
} else {// 下标 大于等于 长度除以2
// 最后一个节点赋值给x
LinkedList.Node<E> x = last;
// 反向遍历对象
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
一开始我对这个逻辑很费解,但是后来思考了一下,他是一个链表的结构,并且存储了第一个节点和最后一个节点作为遍历的开始节点,也就是双链表的结构。这里的index < (size >> 1)是为了判断从哪里开始遍历这个链表才能让,也就是我们常用的二分法,从而降低复杂度从而加快查询的速度。
再看看改的逻辑是什么,基本和取数的逻辑相似,只是做了item属性的替换而已。
/**
* Replaces the element at the specified position in this list with the
* specified element.
*
* @param index index of the element to replace
* @param element element to be stored at the specified position
* @return the element previously at the specified position
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E set(int index, E element) {
checkElementIndex(index);
// 和get逻辑类似
Node<E> x = node(index);
E oldVal = x.item;
// 匹配到就修改他内部的item属性
x.item = element;
return oldVal;
}
删除的逻辑我最后定位都了unlink方法。
/**
* Unlinks non-null node x.
*/
E unlink(LinkedList.Node<E> x) {
// assert x != null;
// x的值赋值个体element
final E element = x.item;
// x的下一个节点赋值给next
final LinkedList.Node<E> next = x.next;
// x的上一个节点赋值给prev
final LinkedList.Node<E> prev = x.prev;
// 上一个节点为空
// 也就是当前节点是第一个节点
if (prev == null) {
// 第一个节点为下一个节点
first = next;
} else {
// 下一个节点赋值给上一个节点的下一个节点
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
这个也很好理解,我们画个图理解一下。
他和ArrayList相同点是,他们都不是真正意义上的删除,被“删除”的空间还是被继续占用,前者是前后节点通知内容的替换,而后者则是本身数组的替换。
参考资料
总结
- LinkedList是链表结构,ArrayList是对象数组。
- LinkedList是双链表结构。
- LinkedList和ArrayLsit都不是实际意义上的删除。
文章中出现的任何错误欢迎指正,共同进步!
最后做个小小广告,有对WEB开发和网络安全感兴趣的,可以加群一起学习和交流!
QQ:425343603