java集合:LinkedList

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java集合:LinkedList相关的知识,希望对你有一定的参考价值。

一、简介。(1.8JDK)

  基于链表实现的方式使得LinkedList在插入和删除时更优于ArrayList,而随机访问则比ArrayList逊色些。

  除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。

  此类实现 Deque 接口,为 add、poll 提供先进先出队列操作,以及其他堆栈和双端队列操作。

  所有操作都是按照双重链接列表的需要执行的。在列表中编索引的操作将从开头或结尾遍历列表(从靠近指定索引的一端)。

二、源码解析。

  (1)三个属性。

  第一个:transient int size = 0;

  第二个:transient Node<E> first;

  第三个:transient Node<E> last;

  三个属性均为设置临时,“transient”为java语言的关键字,变量修饰符,如果用transient声明一个实例变量,当对象存储时,它的值不需要维持。换句话来说就是,用transient关键字标记的成员变量不参与序列化过程。Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。当一个对象被序列化的时候,transient型变量的值不包括在序列化的表示中,然而非transient型的变量是被包括进去的。

  对序列化的理解,先看一段代码。

 1 import java.io.Serializable;
 2 
 3 public class transient_explain implements Serializable{
 4     private static final long serialVersionUID = 996890129747019948L;  
 5     private String name;  
 6     private transient String psw;  
 7   
 8     public transient_explain(String name, String psw) {  
 9         this.name = name;  
10         this.psw = psw;  
11     }  
12   
13     public String toString() {  
14         return "name=" + name + ", psw=" + psw;  
15     }  
16 }
 1 import java.io.FileInputStream;
 2 import java.io.FileOutputStream;
 3 import java.io.ObjectInputStream;
 4 import java.io.ObjectOutputStream;
 5 
 6 public class TestTransient {  
 7     public static void main(String[] args) {  
 8         transient_explain userInfo = new transient_explain("张三", "123456");  
 9         System.out.println(userInfo.toString());  
10         try {  
11             // 序列化,被设置为transient的属性没有被序列化  
12             ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("UserInfo.out"));  
13             o.writeObject(userInfo);
14             o.close();  
15         } catch (Exception e) {  
16             // TODO: handle exception  
17             e.printStackTrace();  
18         }  
19         try {  
20             // 重新读取内容  
21             ObjectInputStream in = new ObjectInputStream(new FileInputStream("UserInfo.out"));
22             transient_explain readUserInfo = (transient_explain) in.readObject();  
23             //读取后psw的内容为null  
24             System.out.println(readUserInfo.toString());
25         } catch (Exception e) {  
26             // TODO: handle exception  
27             e.printStackTrace();  
28         }  
29     }  
30 }  

输出为:

  name=张三, psw=123456
  name=张三, psw=null

  简单的说就是,对象的某个属性没有被序列化就是被“transient”修饰时是不能被写入内存进行永久保存的,这个属性的值是临时的,用的时候存在,不用的时候就消失,不需要维持。

  

  再来看一下Node<E>这个变量,通过ctrl+左击可以得到一段源码。

 1     private static class Node<E> {
 2         E item;
 3         Node<E> next;
 4         Node<E> prev;
 5 
 6         Node(Node<E> prev, E element, Node<E> next) {
 7             this.item = element;
 8             this.next = next;
 9             this.prev = prev;
10         }
11     }

  这个变量由静态内部类实现,它不仅能记录数据,还能记住他的上一个和下一个,在此称它为节点。first与last分别记录链表的表头与表尾。

  (2)两个构造方法。

  第一个:无参构造方法,啥也没有,即用即生成元素Node<E>节点,此时first=last。

 1 public LinkedList() {} 

  第二个:有参构造方法,addAll为添加方法。

1     public LinkedList(Collection<? extends E> c) {
2         this();
3         addAll(c);
4     }
 1     public boolean addAll(Collection<? extends E> c) {
 2         return addAll(size, c);
 3     }

   (3)添加元素的五种方法。

  第一个:添加元素到末尾的方法,size为元素的数目要+1,当添加的元素为该链表的第一个时newNode=last=first三个是一样的,随着元素的添加后边逐渐改变。

1     public boolean add(E e) {
2         linkLast(e);
3         return true;
4     }
 1     void linkLast(E e) {
 2         final Node<E> l = last;
 3         final Node<E> newNode = new Node<>(l, e, null);
 4         last = newNode;
 5         if (l == null)
 6             first = newNode;
 7         else
 8             l.next = newNode;
 9         size++;
10         modCount++;
11     }

  第二个:从前面添加元素。

 1     public void addFirst(E e) {
 2         linkFirst(e);
 3     }
 4     private void linkFirst(E e) {
 5         final Node<E> f = first;
 6         final Node<E> newNode = new Node<>(null, e, f);
 7         first = newNode;
 8         if (f == null)
 9             last = newNode;
10         else
11             f.prev = newNode;
12         size++;
13         modCount++;
14     }

  第三个:和第一个差不多。从后边添加元素,就是没有返回值。

 1     public void addLast(E e) {
 2         linkLast(e);
 3     }
 4     void linkLast(E e) {
 5         final Node<E> l = last;
 6         final Node<E> newNode = new Node<>(l, e, null);
 7         last = newNode;
 8         if (l == null)
 9             first = newNode;
10         else
11             l.next = newNode;
12         size++;
13         modCount++;
14     }

  第四个:if判断是不是往末尾添加元素,假如不是进入分析应该从前面开始定位插入元素位置还是从后面开始定位插入元素(这时的Node<E> first and last尤为重要),这样能使计算机计算量减少许多,特别是对元素特多的链表进行插入操作。这里要强调一个">>" or "<<"这两个使操作二进制的运算符号。

  例:左移运算符,num << 1,相当于num乘以2

    右移运算符,num >> 1,相当于num除以2

 1010      十进制:10     原始数         number
10100      十进制:20     左移一位       number = number << 1;
 1010      十进制:10     右移一位       number = number >> 1;

****** >>>:无符号右移,忽略符号位,空位都以0补齐
 1     public void add(int index, E element) {
 2         checkPositionIndex(index);//这个是检查是否越界
 3 
 4         if (index == size)
 5             linkLast(element);
 6         else
 7             linkBefore(element, node(index));
 8     }
 9     void linkBefore(E e, Node<E> succ) {
10         // assert succ != null;
11         final Node<E> pred = succ.prev;
12         final Node<E> newNode = new Node<>(pred, e, succ);
13         succ.prev = newNode;
14         if (pred == null)
15             first = newNode;
16         else
17             pred.next = newNode;
18         size++;
19         modCount++;
20     }
 1     Node<E> node(int index) {
 2         // assert isElementIndex(index);
 3 
 4         if (index < (size >> 1)) {
 5             Node<E> x = first;
 6             for (int i = 0; i < index; i++)
 7                 x = x.next;
 8             return x;
 9         } else {
10             Node<E> x = last;
11             for (int i = size - 1; i > index; i--)
12                 x = x.prev;
13             return x;
14         }
15     }

   第五个:集合添加与集合插入使用统一代码。在此注意"@SuppressWarnings("unchecked")"使用此语句能提高代码的安全性,相当于批注给编译器一条指令告诉编译器对批注的代码元素内部某些警告保持沉默。

 1     public boolean addAll(Collection<? extends E> c) {
 2         return addAll(size, c);
 3     }
 4     public boolean addAll(int index, Collection<? extends E> c) {
 5         checkPositionIndex(index);
 6 
 7         Object[] a = c.toArray();
 8         int numNew = a.length;
 9         if (numNew == 0)
10             return false;
11 
12         Node<E> pred, succ;
13         if (index == size) {
14             succ = null;
15             pred = last;
16         } else {
17             succ = node(index);
18             pred = succ.prev;
19         }
20 
21         for (Object o : a) {
22             @SuppressWarnings("unchecked") E e = (E) o;
23             Node<E> newNode = new Node<>(pred, e, null);
24             if (pred == null)
25                 first = newNode;
26             else
27                 pred.next = newNode;
28             pred = newNode;
29         }
30 
31         if (succ == null) {
32             last = pred;
33         } else {
34             pred.next = succ;
35             succ.prev = pred;
36         }
37 
38         size += numNew;
39         modCount++;
40         return true;
41     }

  (4)移除的八种方法。

  第一种:移除第一个元素。

1     public E remove() {
2         return removeFirst();
3     }
4     public E removeFirst() {
5         final Node<E> f = first;
6         if (f == null)
7             throw new NoSuchElementException();
8         return unlinkFirst(f);
9     }
 1     private E unlinkFirst(Node<E> f) {
 2         // assert f == first && f != null;
 3         final E element = f.item;
 4         final Node<E> next = f.next;
 5         f.item = null;
 6         f.next = null; // help GC
 7         first = next;
 8         if (next == null)
 9             last = null;
10         else
11             next.prev = null;
12         size--;
13         modCount++;
14         return element;
15     }

  第二种:清除所有元素。

 1     public void clear() {
 2         // Clearing all of the links between nodes is "unnecessary", but:
 3         // - helps a generational GC if the discarded nodes inhabit
 4         //   more than one generation
 5         // - is sure to free memory even if there is a reachable Iterator
 6         for (Node<E> x = first; x != null; ) {
 7             Node<E> next = x.next;
 8             x.item = null;
 9             x.next = null;
10             x.prev = null;
11             x = next;
12         }
13         first = last = null;
14         size = 0;
15         modCount++;
16     }

  第三种:移除此列表中指定位置处的元素。

 1     public E remove(int index) {
 2         checkElementIndex(index);
 3         return unlink(node(index));
 4     }
 5     E unlink(Node<E> x) {
 6         // assert x != null;
 7         final E element = x.item;
 8         final Node<E> next = x.next;
 9         final Node<E> prev = x.prev;
10 
11         if (prev == null) {
12             first = next;
13         } else {
14             prev.next = next;
15             x.prev = null;
16         }
17 
18         if (next == null) {
19             last = prev;
20         } else {
21             next.prev = prev;
22             x.next = null;
23         }
24 
25         x.item = null;
26         size--;
27         modCount++;
28         return element;
29     }

  第四种:注意“==”与“equals”的区别。

 1     public boolean remove(Object o) {
 2         if (o == null) {
 3             for (Node<E> x = first; x != null; x = x.next) {
 4                 if (x.item == null) {
 5                     unlink(x);
 6                     return true;
 7                 }
 8             }
 9         } else {
10             for (Node<E> x = first; x != null; x = x.next) {
11                 if (o.equals(x.item)) {
12                     unlink(x);
13                     return true;
14                 }
15             }
16         }
17         return false;
18     }

  第五种:移除第一个,不解释

1     public E removeFirst() {
2         final Node<E> f = first;
3         if (f == null)
4             throw new NoSuchElementException();
5         return unlinkFirst(f);
6     }

  第六种:移除最后一个,不解释

1     public E removeLast() {
2         final Node<E> l = last;
3         if (l == null)
4             throw new NoSuchElementException();
5         return unlinkLast(l);
6     }

  第八种:从此列表中移除第一次出现的指定元素(从头部到尾部遍历列表时)。

 1 public boolean removeFirstOccurrence(Object o) { 2 return remove(o); 3 } 

  第九种:从此列表中移除最后一次出现的指定元素(从头部到尾部遍历列表时)。

 1     public boolean removeLastOccurrence(Object o) {
 2         if (o == null) {
 3             for (Node<E> x = last; x != null; x = x.prev) {
 4                 if (x.item == null) {
 5                     unlink(x);
 6                     return true;
 7                 }
 8             }
 9         } else {
10             for (Node<E> x = last; x != null; x = x.prev) {
11                 if (o.equals(x.item)) {
12                     unlink(x);
13                     return true;
14                 }
15             }
16         }
17         return false;
18     }

  (5)查找的五种方法。

  第一种:返回此列表中指定位置处的元素

1     public E get(int index) {
2         checkElementIndex(index);
3         return node(index).item;
4     }

  第二种:返回此列表的第一个元素。

1     public E getFirst() {
2         final Node<E> f = first;
3         if (f == null)
4             throw new NoSuchElementException();
5         return f.item;
6     }

  第三种:返回此列表的最后一个元素。

1     public E getLast() {
2         final Node<E> l = last;
3         if (l == null)
4             throw new NoSuchElementException();
5         return l.item;
6     }

  第四种:返回此列表中首次出现的指定元素的索引,如果此列表中不包含该元素,则返回 -1。

 1     public int indexOf(Object o) {
 2         int index = 0;
 3         if (o == null) {
 4             for (Node<E> x = first; x != null; x = x.next) {
 5                 if (x.item == null)
 6                     return index;
 7                 index++;
 8             }
 9         } else {
10             for (Node<E> x = first; x != null; x = x.next) {
11                 if (o.equals(x.item))
12                     return index;
13                 index++;
14             }
15         }
16         return -1;
17     }

  第五种:返回此列表中最后出现的指定元素的索引,如果此列表中不包含该元素,则返回 -1。

 1     public int lastIndexOf(Object o) {
 2         int index = size;
 3         if (o == null) {
 4             for (Node<E> x = last; x != null; x = x.prev) {
 5                 index--;
 6                 if (x.item == null)
 7                     return index;
 8             }
 9         } else {
10             for (Node<E> x = last; x != null; x = x.prev) {
11                 index--;
12                 if (o.equals(x.item))
13                     return index;
14             }
15         }
16         return -1;
17     }

 






以上是关于java集合:LinkedList的主要内容,如果未能解决你的问题,请参考以下文章

Java集合详解2:LinkedList和Queue

Java集合LinkedList详解中篇

ArrayList和LinkedList介绍

JAVA集合LinkedList

Java 集合类学习之LinkedList

Java自学-集合框架 LinkedList