首先要知道,LinkedList实现的是双向循环链表。
链表的组成元素我们称之为节点,节点由三部分组成:前一个节点的引用地址、数据、后一个节点的引用地址。LinkedList的Head节点不包含数据,每一个节点对应一个Entry对象。下面我们通过源码来分析LinkedList的实现原理。
1、Node类源码:
1 private static class Node { 2 Object item; 3 Node next; 4 Node prev; 5 6 Node(Node Node1,Object obj, Node Node2) { 7 item = obj; 8 this.next = node2; 9 this.prev = node1; 10 } 11 }
Node类包含三个属性,其中item存对象数据;next存放后一个节点的信息,通过next可以找到后一个节点;prev存放前一个节点的信息,通过previous可以找到前一个节点。
2、LinkedList的构造方法:LinkedList提供了两个带不同参数的构造方法。
1) LinkedList(),构造一个空列表。
2) LinkedList(Collection<? extends E> c),构造一个包含指定 collection 中的元素的列表,这些元素按其 collection 的迭代器返回的顺序排列。
1 private transient Node header = new Node(null, null, null); //声明一个空的Entry对象 2 private transient int size = 0; //集合中节点的个数 3 4 public LinkedList() { 5 size = 0; //只设置size = 0 6 } 7 8 public LinkedList(Collection<? extends E> c) { 9 this(); //调用不带参数的构造方法,创建一个空的循环链表 10 addAll(c); //调用addAll方法将Collection的元素添加到LinkedList中 11 }
当调用带集合参数的构造方法生成LinkedList对象时,会先调用不带参数的构造方法创建一个空的循环链表,然后调用addAll方法将集合元素添加到LinkedList中。
3、向集合中添加元素:LinkedList提供了多种不同的add方法向集合添加元素。
1) add(E e),将指定元素添加到此列表的结尾。
2) add(int index, E element),在此列表中指定的位置插入指定的元素。
3) addAll(Collection<? extends E> c),添加指定 collection 中的所有元素到此列表的结尾,顺序是指定 collection 的迭代器返回这些元素的顺序。
4) addAll(int index, Collection<? extends E> c),将指定 collection 中的所有元素从指定位置开始插入此列表。
5) addFirst(E e),将指定元素插入此列表的开头。
6) addLast(E e),将指定元素添加到此列表的结尾。
通过源码来分析其底层的实现原理:
1 public boolean add(Object obj) { 2 linkLast(obj); 3 return true; 4 } 5 private void linkFirst(Object obj) { Node node1 = first; Node node2 = new Node(null, obj, node1); first = node2; if(node1 == null) last = node2; else node1.prev = node2; size++; modCount++; } void linkLast(Object obj) { Node node1 = last; //初始化的时候last为null Node node2 = new Node(node1, obj, null); //刚开始的时候直接添加obj last = node2; //把第一个放进来的存obj的Node设为last if(node1 == null) first = node2; //把第一个Node设为first else node1.next = node2; size++; modCount++; }
这个过程很简单,基本的数据结构
add 另一个方法和addAll()方法
1 public void add(int i, Object obj) { 2 checkPositionIndex(i); if(i == size) linkLast(obj); else linkBefore(obj, node(i)); 3 }
//判断是否index越界也就是size小于i
private void checkPositionIndex(int i) { if(!isPositionIndex(i)) throw new IndexOutOfBoundsException(outOfBoundsMsg(i)); else return; }
private boolean isPositionIndex(int i) { return i >= 0 && i <= size; }
//查找要插入的Node节点
Node node(int i) { if(i < size >> 1) { Node node1 = first; for(int j = 0; j < i; j++) node1 = node1.next; return node1; } Node node2 = last; for(int k = size - 1; k > i; k--) node2 = node2.prev; return node2; }
//在Node1之前插入
void linkBefore(Object obj, Node node1)
{
Node node2 = node1.prev;
Node node3 = new Node(node2, obj, node1); //创建一个Node3 前驱为Node2,后为Node1
node1.prev = node3;
if(node2 == null)
first = node3;
else
node2.next = node3;
size++;
modCount++;
}
1 public boolean addAll(Collection<? extends E> c) {
2 return addAll(size, c); 3 } 4 5 public boolean addAll(int index, Collection<? extends E> c) { 6 checkPositionIndex(i); 9 Object aobj[] = collection.toArray(); int j = aobj.length; if(j == 0) return false; Node node1; //保存前驱和后继节点 Node node2;
//判断是在尾部还是中间和前面插入 个人觉得有点多余 因为是在size后面添加 if(i == size) { node2 = null; node1 = last; } else { node2 = node(i); node1 = node2.prev; } Object aobj1[] = aobj; int k = aobj1.length; for(int l = 0; l < k; l++) { Object obj = aobj1[l]; Object obj1 = obj; Node node3 = new Node(node1, obj1, null); if(node1 == null) first = node3; else node1.next = node3; node1 = node3; } if(node2 == null) { last = node1; } else { node1.next = node2; node2.prev = node1; } size += j; modCount++; return true; 26 }
4、获取LinkedList中的元素:
get方法更简单,先检查是否越界,然后直接Node(int i).Item就可以得到了。
5、移除LinkedList中的元素:
1) remove(int index),移除此列表中指定位置处的元素。
2) remove(Object o),从此列表中移除首次出现的指定元素(如果存在)。