介绍

LinkList也是工作中常见的集合, 底层使用双向链表结构 比较适合新增和删除, 查询和修改需要遍历相对ArrayList比较消耗性能

内部类 Node

  1. private static class Node<E> {
  2. // 元素值
  3. E item;
  4. // 下一个节点
  5. Node<E> next;
  6. // 上一个几点
  7. Node<E> prev;
  8. // 构造一个新节点 指向上一个节点和下一个节点
  9. Node(Node<E> prev, E element, Node<E> next) {
  10. this.item = element;
  11. this.next = next;
  12. this.prev = prev;
  13. }
  14. }

add 新增

通过代码可以看出, 在新增元素时只需要创建一个新节点 Node, 并将原始链表最后一个Node的next指向新Node

  1. public boolean add(E e) {
  2. linkLast(e);
  3. return true;
  4. }
  5. /**
  6. * Links e as last element.
  7. */
  8. void linkLast(E e) {
  9. // 声明 l 为最后一个节点
  10. final Node<E> l = last;
  11. // 创建新节点, 指向上一个节点, 下一个节点为空
  12. final Node<E> newNode = new Node<>(l, e, null);
  13. // 最后一个节点为新创建的节点
  14. last = newNode;
  15. // 判断是否为第一个元素, 否则将 新创建的 Node加入链表
  16. if (l == null)
  17. first = newNode;
  18. else
  19. l.next = newNode;
  20. size++;
  21. modCount++;
  22. }

remove 删除

1.删除操作需要遍历链表找到相应元素, 然后移动指针即可
2.删除首尾元素直接移动指针即可 removeFirst()/removeLast() 方法

  1. public boolean remove(Object o) {
  2. if (o == null) {
  3. // 遍历链表
  4. for (Node<E> x = first; x != null; x = x.next) {
  5. if (x.item == null) {
  6. unlink(x);
  7. return true;
  8. }
  9. }
  10. } else {
  11. for (Node<E> x = first; x != null; x = x.next) {
  12. if (o.equals(x.item)) {
  13. unlink(x);
  14. return true;
  15. }
  16. }
  17. }
  18. return false;
  19. }
  20. /**
  21. * 删除元素
  22. */
  23. E unlink(Node<E> x) {
  24. // assert x != null;
  25. final E element = x.item;
  26. final Node<E> next = x.next;
  27. final Node<E> prev = x.prev;
  28. // 判断上一个Node是否为空
  29. if (prev == null) {
  30. // 空, 该节点为链表头, 将下一个节点设置为链表头
  31. first = next;
  32. } else {
  33. // 不为空, 将上一个节点的next 指向当前节点的 next, 并将当前节点的 prev置为空
  34. prev.next = next;
  35. x.prev = null;
  36. }
  37. // 判断下一个Node是否为空
  38. if (next == null) {
  39. // 空, 该节点为链表尾, 将链表尾设置为当前节点的上一个节点
  40. last = prev;
  41. } else {
  42. // 不为空, 将下一个节点的prev, 设置为上一个节点, 并将当前节点的 next置为空
  43. next.prev = prev;
  44. x.next = null;
  45. }
  46. x.item = null;
  47. size--;
  48. modCount++;
  49. return element;
  50. }

get/set

get/set时都需要获取指定索引的元素, 使用二分法查找, 然后进行遍历查找, 所以此处相较于ArrayList多了遍历查询, 虽然使用了二分法进行优化, 但是get/set操作相比ArrayList来说性能还是相对较差

  1. public E get(int index) {
  2. // 校验索引
  3. checkElementIndex(index);
  4. // 二分法遍历查找节点
  5. return node(index).item;
  6. }
  7. public E set(int index, E element) {
  8. // 校验索引
  9. checkElementIndex(index);
  10. // 二分法遍历查找节点
  11. Node<E> x = node(index);
  12. // 修改Node节点的 item
  13. E oldVal = x.item;
  14. x.item = element;
  15. return oldVal;
  16. }
  17. /**
  18. * 返回指定索引处非null节点.
  19. */
  20. Node<E> node(int index) {
  21. // assert isElementIndex(index);
  22. // 判断索引是否小于长度的一半 (二分法) 然后遍历查找
  23. if (index < (size >> 1)) {
  24. Node<E> x = first;
  25. for (int i = 0; i < index; i++)
  26. x = x.next;
  27. return x;
  28. } else {
  29. Node<E> x = last;
  30. for (int i = size - 1; i > index; i--)
  31. x = x.prev;
  32. return x;
  33. }
  34. }