LinkedList——链表
相比较于ArrayList的方法,除了Add(),get(),remove(),size()等,LinkedList多了addLast(),addFirst(),getFirst
1、一般将数据结构分为两大类:线性数据结构和非线性数据结构。线性数据结构有线性表、栈、队列、串、数组和文件;非线性数据结构有树和图。
线性表的逻辑结构是n个数据元素的有限序列: (a1, a2,a3,…an),n为线性表的长度(n≥0),n=0的表称为空表。数据元素呈线性关系。必存在唯一的称为“第一个”的数据元素;必存在唯一的称为“最后一个”的数据元素;除第一个元素外,每个元素都有且只有一个前驱元素;除最后一个元素外,每个元素都有且只有一个后继元素。所有数据元素在同一个线性表中必须是相同的数据类型。
线性表按其存储结构可分为顺序表和链表。用顺序存储结构存储的线性表称为顺序表;用链式存储结构存储的线性表称为链表。将线性表中的数据元素依次存放在某个存储区域中,所形成的表称为顺序表。一维数组就是用顺序方式存储的线性表。ArrayList就相当于顺序表。LinkedList就相当于链表。
2、单向链表
链表的操作
[java] view plain copy print ?
- public class Node
- {
- String data;
- Node next;
-
- public Node(String s)
- {
- data = s;
- }
- }
-
- public class NodeTest
- {
- public static void main(String[] args)
- {
- Node node1 = new Node("node1");
- Node node2 = new Node("node2");
- Node node3 = new Node("node3");
- Node node4 = new Node("node4");
-
- node1.next =node2;
- node2.next =node3;
-
- node1.next = node4;
- node4.next = node2;
-
- System.out.println(node1.next.data);
-
- node1.next = node2;
- node4.next = null;
-
- System.out.println(node1.next.data);
- }
- }
public class Node
{
String data;
Node next;
public Node(String s)
{
data = s;
}
}
public class NodeTest
{
public static void main(String[] args)
{
Node node1 = new Node("node1");
Node node2 = new Node("node2");
Node node3 = new Node("node3");
Node node4 = new Node("node4");
node1.next =node2;
node2.next =node3;
node1.next = node4;
node4.next = node2;
System.out.println(node1.next.data);
node1.next = node2;
node4.next = null;
System.out.println(node1.next.data);
}
}
3、循环链表
双向循环列表
[java] view plain copy print ?
- public class Node2
- {
- Node2 previous;
- Node2 next;
- String data;
-
- public Node2(String s)
- {
- data = s;
- }
- }
-
- public class NodeTest2
- {
- public static void main(String[] args)
- {
- Node2 node1 = new Node2("node1");
- Node2 node2 = new Node2("node2");
- Node2 node3 = new Node2("node3");
-
- node1.next =node2;
- node1.previous = node3;
- node2.next =node3;
- node2.previous = node1;
- node3.next = node1;
- node3.previous = node2;
-
- Node2 node4 = new Node2("node4");
-
- node1.next = node4;
- node4.previous =node1;
-
- node2.previous =node4;
- node4.next =node2;
-
- System.out.println("--------------");
-
- node1.next = node2;
- node2.previous =node1;
-
- node4.next =null;
- node3.previous =null;
-
-
- }
- }
public class Node2
{
Node2 previous;
Node2 next;
String data;
public Node2(String s)
{
data = s;
}
}
public class NodeTest2
{
public static void main(String[] args)
{
Node2 node1 = new Node2("node1");
Node2 node2 = new Node2("node2");
Node2 node3 = new Node2("node3");
node1.next =node2;
node1.previous = node3;
node2.next =node3;
node2.previous = node1;
node3.next = node1;
node3.previous = node2;
//在node1和node2之间插入node4
Node2 node4 = new Node2("node4");
node1.next = node4;
node4.previous =node1;
node2.previous =node4;
node4.next =node2;
System.out.println("--------------");
//删除node4
node1.next = node2;
node2.previous =node1;
node4.next =null;
node3.previous =null;
}
}
4、LinkedList源代码分析:LinkedList底层就是使用了双向循环链表实现
[java] view plain copy print ?
-
- private transient Entry<E> header = new Entry<E>(null, null, null);
- private transient int size = 0;
-
- public LinkedList() {
- header.next = header.previous = header;
- }
//先定义两个成员变量
private transient Entry<E> header = new Entry<E>(null, null, null);
private transient int size = 0;
//默认构造函数
public LinkedList() {
header.next = header.previous = header;
}
看一下Entry:
[java] view plain copy print ?
- private static class Entry<E> {
- E element;
- Entry<E> next;
- Entry<E> previous;
-
- Entry(E element, Entry<E> next, Entry<E> previous) {
- this.element = element;
- this.next = next;
- this.previous = previous;
- }
- }
private static class Entry<E> {
E element;
Entry<E> next;
Entry<E> previous;
Entry(E element, Entry<E> next, Entry<E> previous) {
this.element = element;
this.next = next;
this.previous = previous;
}
}
它的定义就像Node2,有一个存放数据的成员变量element,还各有一个指向前驱和后继Entry的指针previous和next。
关于add()方法,参数是一个Object对象,作为Entry的element值
[java] view plain copy print ?
- public boolean add(E e) {
- addBefore(e, header);
- return true;
- }
-
- private Entry<E> addBefore(E e, Entry<E> entry) {
- Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
- newEntry.previous.next = newEntry;
- newEntry.next.previous = newEntry;
- size++;
- modCount++;
- return newEntry;
- }
public boolean add(E e) {
addBefore(e, header);
return true;
}
private Entry<E> addBefore(E e, Entry<E> entry) {
Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
newEntry.previous.next = newEntry;
newEntry.next.previous = newEntry;
size++;
modCount++;
return newEntry;
}
add()方法默认调用addBefore()方法插入一个新的节点,addBefore看其字面意思,就是在指定的节点之前插入,而其参数默认为e和header,e是要保存的数据,header是链表的头节点,按照上面的图,头节点的前驱就是链表的最后一个节点,所以插入默认是插入到链表最后。addBefore方法先构建一个新的Entry节点,根据其参数,这个新节点的element=e,next=entry,(也就是header),previous=entry.previous,(也就是header.previous)。然后修改原来最后一个节点的后继指向新节点:newEntry.previous.next = newEntry;然后在修改原来的头节点的前驱指向新节点:newEntry.next.previous = newEntry;
5、关于ArrayList与LinkedList的比较分析
1)ArrayList底层采用数组实现,LinkedList底层采用双向链表实现。
2)当执行插入或删除操作时,采用LinkedList比较好。
3)当执行搜索操作时,采用ArrayList比较好。
6、list的删除操作只删除找到匹配的第一个节点,如linkedlist.add("aaa");linkedlist.add("aaa");删除时,remove("aaa")只删除第一个找到的aaa节点。
在get()方法取第n个元素时,调用了entry()方法,这个方法使用了一个优化性能的算法,先判断n是否大于链表元素个数的二分之一,如果不大于,说明距离头节点比较近,从头开始先后遍历寻找,否则说明距离末节点比较近,从尾部开始向前遍历寻找。
7、当向ArrayList添加一个对象时,实际上就是将该对象放置到了ArrayList底层所维护的数组当中;当向LinkedList中添加一个对象时,实际上LinkedList内部会生成一个Entry对象,该对象的结构为:
Entry
{
Entry previous;
Object element;
Entry next;
}
其中的Object类型的元素element就是我们向LinkedList中所添加的元素,然后Entry又构造好了向前与向后的引用previous、next,最后将生成的这个Entry对象加入到了链表当中。换句话说,LinkedList中所维护的是一个个的Entry对象。