Java集合之LinkedList常用方法解析

  最近正准备回顾一下Java,所以在此做一些记录。

1.LinkedList使用的是链表结构,先看一下节点的定义

 1  /**
 2      *
 3      * 连接的节点
 4      */
 5     private static class Node<E> {
 6         //保存的数据
 7         E item;
 8         //后置节点
 9         Node<E> next;
10         //前置节点
11         Node<E> prev;
12         //构造方法
13         Node(Node<E> prev, E element, Node<E> next) {
14             this.item = element;
15             this.next = next;
16             this.prev = prev;
17         }
18     }
View Code

2.add(E e) 添加一个元素

 1  /**
 2      * 添加一个元素
 3      *
 4      * @param e 添加的元素
 5      */
 6     public boolean add(E e) {
 7         //调用添加到末尾
 8         linkLast(e);
 9         return true;
10     }
11 
12 
13 /**
14      * 添加一个元素到链表的尾部
15      */
16     void linkLast(E e) {
17         //获取链表的尾部节点
18         final Node<E> l = last;
19         //创建新的节点,前置节点为last,后置节点为空
20         final Node<E> newNode = new Node<>(l, e, null);
21         //重新设置尾部节点
22         last = newNode;
23         //如果之前的尾部节点为空,说明之前没有元素
24         if (l == null)
25             //设置头节点也为空
26             first = newNode;
27         else
28             //如果之前的尾部节点不为空,则为之前的尾部节点追加后置节点
29             l.next = newNode;
30         //数量加一
31         size++;
32         modCount++;
33     }   
34  
View Code

3.add(int index, E element)  添加一个元素到指定位置

 1  /**
 2      * 添加一个元素到指定位置
 3      *
 4      * @param index 指定的位置
 5      * @param element 添加的元素
 6      */
 7     public void add(int index, E element) {
 8         //检查指定的位置是否越界
 9         checkPositionIndex(index);
10         //如果插入的是尾部,直接调用插入尾部的方法
11         if (index == size)
12             linkLast(element);
13         else
14             //需要先找到指定位置的节点
15             // 插入到该节点之前
16             linkBefore(element, node(index));
17     }
18 
19  //检查是否越界
20     private void checkPositionIndex(int index) {
21         if (!isPositionIndex(index))
22             throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
23     }
24 
25   private boolean isPositionIndex(int index) {
26         return index >= 0 && index <= size;
27     }
28 
29 /**
30      * 查询指定位置的节点
31      */
32     Node<E> node(int index) {
33         // 类似于二分查询
34         // 如果位置小于集合大小的一半,从头节点开始遍历
35         // 否则从尾部节点开始遍历
36 
37         if (index < (size >> 1)) {
38             Node<E> x = first;
39             for (int i = 0; i < index; i++)
40                 x = x.next;
41             return x;
42         } else {
43             Node<E> x = last;
44             for (int i = size - 1; i > index; i--)
45                 x = x.prev;
46             return x;
47         }
48     }
49 
50   /**
51      * 在某个节点之前进行插入
52      */
53     void linkBefore(E e, Node<E> succ) {
54         // 获取当前节点的前置节点
55         final Node<E> pred = succ.prev;
56         //保存创建的新节点
57         final Node<E> newNode = new Node<>(pred, e, succ);
58         //将新节点设置为当前位置节点的前置节点
59         succ.prev = newNode;
60         //如果当前位置节点的前置节点为空,则说明当前位置为头节点
61         if (pred == null)
62             //重新设置头节点
63             first = newNode;
64         else
65             //将当前位置节点的前置节点的后置节点设置为新节点
66             pred.next = newNode;
67         //数量增加
68         size++;
69         modCount++;
70     }
View Code

4.get(int index) 获取指定位置的元素

 1 /**
 2      * 获取指定位置的元素
 3      *
 4      * @param index 指定的位置
 5      * @return 查询到的元素
 6      */
 7     public E get(int index) {
 8         //判断是否越界
 9         checkElementIndex(index);
10         //查询到节点的值
11         return node(index).item;
12     }
13 
14 //检查是否越界
15     private void checkElementIndex(int index) {
16         if (!isElementIndex(index))
17             throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
18     }
19 
20   private boolean isElementIndex(int index) {
21         return index >= 0 && index < size;
22     }
23 
24  /**
25      * 查询指定位置的节点
26      */
27     Node<E> node(int index) {
28         // 类似于二分查询
29         // 如果位置小于集合大小的一半,从头节点开始遍历
30         // 否则从尾部节点开始遍历
31 
32         if (index < (size >> 1)) {
33             Node<E> x = first;
34             for (int i = 0; i < index; i++)
35                 x = x.next;
36             return x;
37         } else {
38             Node<E> x = last;
39             for (int i = size - 1; i > index; i--)
40                 x = x.prev;
41             return x;
42         }
43     }
View Code

5.remove(Object o) 删除指定的元素

 1  /**
 2      * 删除指定的元素
 3      *
 4      * @param o 需要删除的元素
 5      */
 6     public boolean remove(Object o) {
 7         //判断需要删除的元素是否为空
 8         if (o == null) {
 9             //如果为空,从头节点开始遍历
10             for (Node<E> x = first; x != null; x = x.next) {
11                 if (x.item == null) {
12                     //删除节点
13                     unlink(x);
14                     return true;
15                 }
16             }
17         } else {
18             //删除的元素不为为空,也是头节点开始遍历
19             for (Node<E> x = first; x != null; x = x.next) {
20                 if (o.equals(x.item)) {
21                     //删除节点
22                     unlink(x);
23                     return true;
24                 }
25             }
26         }
27         return false;
28     }
29 
30  /**
31      * 删除一个节点
32      */
33     E unlink(Node<E> x) {
34         // 获取删除节点的值
35         final E element = x.item;
36         //获取删除节点的后置节点
37         final Node<E> next = x.next;
38         //获取删除节点的前置节点
39         final Node<E> prev = x.prev;
40 
41         //判断前置节点是否为空
42         if (prev == null) {
43             //为空则其后置节点晋升为头节点
44             first = next;
45         } else {
46             //修改前置节点的后置节点
47             prev.next = next;
48             //将删除节点的前置节点置空
49             x.prev = null;
50         }
51 
52         //判断后置节点是否为空
53         if (next == null) {
54             //后置节点为空,则其前置节点修改为尾部节点
55             last = prev;
56         } else {
57             //后置节点的前置节点修改
58             next.prev = prev;
59             //删除节点的后置节点置空
60             x.next = null;
61         }
62         //删除节点的值设置为空
63         x.item = null;
64         //数量减1
65         size--;
66         modCount++;
67         return element;
68     }
View Code

6.set(int index, E element) 在指定位置节点设置值

 1  /**
 2      * 在指定位置节点设置值
 3      *
 4      * @param index 指定的位置
 5      * @param element 元素值
 6      */
 7     public E set(int index, E element) {
 8         //检查位置是否越界
 9         checkElementIndex(index);
10         //查询到当前位置的节点
11         Node<E> x = node(index);
12         //获取旧值
13         E oldVal = x.item;
14         //设置新值
15         x.item = element;
16         //返回旧值
17         return oldVal;
18     }
19 
20  /**
21      * 查询指定位置的节点
22      */
23     Node<E> node(int index) {
24         // 类似于二分查询
25         // 如果位置小于集合大小的一半,从头节点开始遍历
26         // 否则从尾部节点开始遍历
27 
28         if (index < (size >> 1)) {
29             Node<E> x = first;
30             for (int i = 0; i < index; i++)
31                 x = x.next;
32             return x;
33         } else {
34             Node<E> x = last;
35             for (int i = size - 1; i > index; i--)
36                 x = x.prev;
37             return x;
38         }
39     }
View Code

7.indexOf(Object o) 查询指定元素值所在位置,lastIndexOf也一样,只是从尾部节点开始查询

 1     /**
 2      * 查询指定元素值所在位置
 3      *
 4      * @param o 元素值
 5      */
 6     public int indexOf(Object o) {
 7         int index = 0;
 8         //判断查询的值是否为null
 9         if (o == null) {
10             //从头节点开始遍历查询
11             for (Node<E> x = first; x != null; x = x.next) {
12                 if (x.item == null)
13                     return index;
14                 index++;
15             }
16         } else {
17             //从头节点开始遍历查询
18             for (Node<E> x = first; x != null; x = x.next) {
19                 if (o.equals(x.item))
20                     return index;
21                 index++;
22             }
23         }
24         return -1;
25     }
View Code

8.peek() 获取头节点的元素值,但不删除头节点

   poll() 获取头节点的元素值,并删除头节点

 pop() 获取头节点的元素值,并删除头节点,头节点为空则抛出异常

 1     /**
 2      * 获取头节点的元素值,但不删除头节点
 3      *
 4      */
 5     public E peek() {
 6         final Node<E> f = first;
 7         return (f == null) ? null : f.item;
 8     }
 9 
10  /**
11      * 获取头节点的元素值,并删除头节点
12      */
13     public E poll() {
14         final Node<E> f = first;
15         return (f == null) ? null : unlinkFirst(f);
16     }
17 
18  /**
19      * 删除头节点
20      */
21     private E unlinkFirst(Node<E> f) {
22         //获取节点的值
23         final E element = f.item;
24         //获取到后置节点
25         final Node<E> next = f.next;
26         //清空节点的值和后置节点
27         f.item = null;
28         f.next = null;
29         //重新设置头节点
30         first = next;
31         //判断后置节点是否为null
32         if (next == null)
33             //说明只有这么一个节点,尾部节点设置null
34             last = null;
35         else
36             //已经设置为头节点了,所以清空前置节点
37             next.prev = null;
38         //数量减1
39         size--;
40         modCount++;
41         return element;
42     }
43 
44 /**
45      * 获取头节点的元素值,并删除头节点
46      */
47     public E pop() {
48         return removeFirst();
49     }
50 
51  /**
52      * 删除头节点
53      */
54     public E removeFirst() {
55         final Node<E> f = first;
56         //头节点为空则抛出异常
57         if (f == null)
58             throw new NoSuchElementException();
59         //删除头节点
60         return unlinkFirst(f);
61     }
View Code

9.offer(E e)  添加新元素到末尾

  push(E e) 添加新元素到头节点

 1 /**
 2      * 添加新元素到尾部
 3      *
 4      * @param e 新元素
 5      */
 6     public boolean offer(E e) {
 7         return add(e);
 8     }
 9 
10 
11     /**
12      * 添加一个元素
13      *
14      * @param e 添加的元素
15      */
16     public boolean add(E e) {
17         //调用添加到末尾
18         linkLast(e);
19         return true;
20     }
21 
22   /**
23      * 添加一个元素到链表的尾部
24      */
25     void linkLast(E e) {
26         //获取链表的尾部节点
27         final Node<E> l = last;
28         //创建新的节点,前置节点为last,后置节点为空
29         final Node<E> newNode = new Node<>(l, e, null);
30         //重新设置尾部节点
31         last = newNode;
32         //如果之前的尾部节点为空,说明之前没有元素
33         if (l == null)
34             //设置头节点也为空
35             first = newNode;
36         else
37             //如果之前的尾部节点不为空,则为之前的尾部节点追加后置节点
38             l.next = newNode;
39         //数量加一
40         size++;
41         modCount++;
42     }
43 
44 /**
45      * 添加新元素到头节点
46      *
47      * @param e the element to push
48      * @since 1.6
49      */
50     public void push(E e) {
51         addFirst(e);
52     }
53 
54 
55     /**
56      * 添加元素到头节点
57      *
58      * @param e 新增的元素
59      */
60     public void addFirst(E e) {
61         //添加元素到头节点
62         linkFirst(e);
63     }
64 
65  /**
66      * 添加元素到头节点
67      */
68     private void linkFirst(E e) {
69         //获取当前的头节点
70         final Node<E> f = first;
71         //创建新节点
72         final Node<E> newNode = new Node<>(null, e, f);
73         //设置新节点为头节点
74         first = newNode;
75         //判断头节点是否为空
76         if (f == null)
77             //头节点为空,说明之前没有节点,新节点同时为尾部节点
78             last = newNode;
79         else
80             //头节点不为空,则头节点的前置节点指向新节点
81             f.prev = newNode;
82         //数量加一
83         size++;
84         modCount++;
85     }
View Code

 总结一下

1.LinkedList作为双向链表,维护了头尾节点,头尾节点的插入比较方便,中间数据的插入需要遍历查询再做插入

2.查询指定位置时,使用了一次二分,最多需要遍历一半的节点

原文地址:https://www.cnblogs.com/hdllhd/p/11771218.html