########LinkedList特点和底层实现########
LinkedList底层是双向链表实现的存储。特点:查询效率低,增删效率高,线程不安全;
因此一般情况下不常用;
下面这个图很重要,需理解
每一个节点(Node或者Entry)都包含三个部分----上、数据、下
增加:把相邻的两个链表拆开指向我;
删除:a1指向a3,a2指向空格;这就是链表的好处,很简单
########LinkedList手工实现########
一:第一个版本
首先创建一个Node的文件,里面构造了一个Node
package cn.sxt.mycollection;
public class Node {
Node previous; //上一个节点
Node next; //下一个节点
Object element;//元素数据
public Node(Node previous, Node next, Object element) {
super();
this.previous = previous;
this.next = next;
this.element = element;
}
public Node(Object element) {
super();
this.element = element;
}
}
Node里面有两个构造方法!!!
然后自定义一个链表
package cn.sxt.mycollection;
/**
* 自定义一个链表
* @author 韩文韬
*
*/
public class SxtLinkedList01 {
private Node first;
private Node last;
private int size;
//现在是空的,目前要往里面加内容
//["a"]
public void add(Object obj) {
Node node=new Node(obj);
if(first==null)
{
// node.previous=null;
// node.next=null;
first=node;
last=node;
}
else
{
node.previous=last;
node.next=null;
last.next=node; //最后一个指向node
last=node; //最后一个变为node
}
}
public static void main(String[] args) {
SxtLinkedList01 list=new SxtLinkedList01();
list.add("a");
list.add("b");
list.add("c");
System.out.println(list);
}
}
我们来一段段分析这片代码
if(first==null)
{
// node.previous=null;
// node.next=null;
first=node;
last=node;
}
当插入第一个节点的时候,首是这个节点,尾也是这个节点,该节点前是空,尾也是空;
else
{
node.previous=last;
node.next=null;
last.next=node; //最后一个指向node
last=node; //最后一个变为node
}
当插入之后的节点时,该节点前是之前最后一个节点(last),尾是空,最后一个指向node,然后再将最后一个变为node;
然后创建一个list对象,利用add方法添加元素,而后打印;
输出结果为:cn.sxt.mycollection.SxtLinkedList01@311d617d
这表明我们需要重写toString方法;
debug一下我们发现如下图:
这表明链表已经构造成功,但是输出方法不对而已~
打印我们怎么打印呢,从first开始打印,然后.next一直往下找,假设链表里是[a,b,c],那么我们先打印a,first=a,然后b,然后c,last=c;
下面重写toString方法,将链表完整的打印出来
@Override //重写toString方法,为了输出
public String toString() {
StringBuilder sb=new StringBuilder();
sb.append('[');
//[a,b,c] first=a,last=c
Node temp=first; //定义一个临时节点
while(temp!=null)
{
sb.append(temp.element+",");
temp=temp.next;
}
sb.setCharAt(sb.length()-1, ']');
return sb.toString();
}
然后运行,输出结果为:[a,b,c]
至此,第一个版本结束!
完整版本代码如下:
package cn.sxt.mycollection;
/**
* 自定义一个链表
* @author 韩文韬
*
*/
public class SxtLinkedList01 {
private Node first;
private Node last;
private int size;
//现在是空的,目前要往里面加内容
//["a"]
public void add(Object obj) {
Node node=new Node(obj);
if(first==null)
{
// node.previous=null;
// node.next=null;
first=node;
last=node;
}
else
{
node.previous=last;
node.next=null;
last.next=node; //最后一个指向node
last=node; //最后一个变为node
}
size++;
}
@Override //重写toString方法,为了输出
public String toString() {
StringBuilder sb=new StringBuilder();
sb.append('[');
//[a,b,c] first=a,last=c
Node temp=first; //定义一个临时节点
while(temp!=null)
{
sb.append(temp.element+",");
temp=temp.next;
}
sb.setCharAt(sb.length()-1, ']');
return sb.toString();
}
public static void main(String[] args) {
SxtLinkedList01 list=new SxtLinkedList01();
list.add("a");
list.add("b");
list.add("c");
System.out.println(list);
}
}
二:第二个版本
第二个版本讲get方法,get方法是根据你传入的索引位置返回对应的元素;
public Object get(int index) {
if(index<0||index>size-1)
{
//不合法
throw new RuntimeException("索引不合法!"+index);
}
Node temp=first;
if(index<(size>>1)) //size>>1相当于除以2
{
for(int i=0;i<index;i++)
{
temp=temp.next;
}
}
else
{
temp=last;
for(int i=size-1;i>index;i--)
{
temp=temp.previous;
}
}
return temp.element;
}
这里用了一个更为高效的办法,即把区间分为两半,分别遍历;
完整代码如下:
package cn.sxt.mycollection;
/**
* 自定义一个链表
* 增加get方法
* @author 韩文韬
*
*/
public class SxtLinkedList02 {
private Node first;
private Node last;
private int size;
//现在是空的,目前要往里面加内容
//["a"]
public void add(Object obj) {
Node node=new Node(obj);
if(first==null)
{
// node.previous=null;
// node.next=null;
first=node;
last=node;
}
else
{
node.previous=last;
node.next=null;
last.next=node; //最后一个指向node
last=node; //最后一个变为node
}
size++;
}
@Override //重写toString方法,为了输出
public String toString() {
StringBuilder sb=new StringBuilder();
sb.append('[');
//[a,b,c] first=a,last=c
Node temp=first; //定义一个临时节点
while(temp!=null)
{
sb.append(temp.element+",");
temp=temp.next;
}
sb.setCharAt(sb.length()-1, ']');
return sb.toString();
}
public Object get(int index) {
if(index<0||index>size-1) //这里有问题
{
//不合法
throw new RuntimeException("索引不合法!"+index);
}
Node temp=first;
if(index<(size>>1)) //size>>1相当于除以2
{
for(int i=0;i<index;i++)
{
temp=temp.next;
}
}
else
{
temp=last;
for(int i=size-1;i>index;i--)
{
temp=temp.previous;
}
}
return temp.element;
}
public static void main(String[] args) {
SxtLinkedList02 list=new SxtLinkedList02();
list.add("a");
list.add("b");
list.add("c");
System.out.println(list.get(2));
}
}
三:第三个版本
这个版本讲如何使用remove方法;
有一块封装一下便于重用,如下:
public Node getNode(int index) {
Node temp=first;
if(index<(size>>1)) //size>>1相当于除以2
{
for(int i=0;i<index;i++)
{
temp=temp.next;
}
}
else
{
temp=last;
for(int i=size-1;i>index;i--)
{
temp=temp.previous;
}
}
return temp;
}
根据一个索引便可获得其值;
下面是完整代码
package cn.sxt.mycollection;
/**
* 自定义一个链表
* 增加get方法
* 增加remove方法
* @author 韩文韬
*
*/
public class SxtLinkedList03 {
private Node first;
private Node last;
private int size;
//现在是空的,目前要往里面加内容
//["a"]
public void add(Object obj) {
Node node=new Node(obj);
if(first==null)
{
// node.previous=null;
// node.next=null;
first=node;
last=node;
}
else
{
node.previous=last;
node.next=null;
last.next=node; //最后一个指向node
last=node; //最后一个变为node
}
size++;
}
@Override //重写toString方法,为了输出
public String toString() {
StringBuilder sb=new StringBuilder();
sb.append('[');
//[a,b,c] first=a,last=c
Node temp=first; //定义一个临时节点
while(temp!=null)
{
sb.append(temp.element+",");
temp=temp.next;
}
sb.setCharAt(sb.length()-1, ']');
return sb.toString();
}
// public int size() {
// return size;
// }
public Object get(int index) {
if(index<0||index>size-1) //这里有问题
{
//不合法
throw new RuntimeException("索引不合法!"+index);
}
Node temp=getNode(index);
return temp!=null?temp.element:null;
}
public Node getNode(int index) {
Node temp=first;
if(index<(size>>1)) //size>>1相当于除以2
{
for(int i=0;i<index;i++)
{
temp=temp.next;
}
}
else
{
temp=last;
for(int i=size-1;i>index;i--)
{
temp=temp.previous;
}
}
return temp;
}
public void remove(int index)
{
Node temp=getNode(index);
if(temp!=null)
{
Node up=temp.previous;
Node down=temp.next;
if(up!=null)
{
up.next=down;
}
if(down!=null)
{
down.previous=up;
}
//被删除的元素是第一个元素时
if(index==0)
{
first=down;
}
//被删除的元素时最后一个元素时
if(index==size-1)
{
last=up;
}
size--;
}
}
public static void main(String[] args) {
SxtLinkedList03 list=new SxtLinkedList03();
list.add("a");
list.add("b");
list.add("c");
//System.out.println(list);
list.remove(2);
System.out.println(list);
}
}
四:第四个版本
这个版本主要为了解释插入add方法,add部分代码如下所示:
public void add(int index,Object obj)
{
Node newNode=new Node(obj);
if(index>=0&&index<=size-1)
{
Node temp=getNode(index);
Node up=temp.previous;
if(up!=null)
{
up.next=newNode;
newNode.previous=up;
temp.previous=newNode;
newNode.next=temp;
}
//在开头插入时
if(index==0)
{
first=newNode;
temp.previous=newNode;
newNode.next=temp;
}
}
//在末尾插入时
if(index==size)
{
Node temp=getNode(size-1);
temp.next=newNode;
newNode.previous=temp;
}
}
这部分代码为我自己补充,其中比较完整的包括了开头插入与末尾插入的情况,完整代码如下:
package cn.sxt.mycollection;
/**
* 自定义一个链表
* 增加get方法
* 增加remove方法
* 增加插入方法
* @author 韩文韬
*
*/
public class SxtLinkedList04 {
private Node first;
private Node last;
private int size;
//现在是空的,目前要往里面加内容
//["a"]
public void add(Object obj) {
Node node=new Node(obj);
if(first==null)
{
// node.previous=null;
// node.next=null;
first=node;
last=node;
}
else
{
node.previous=last;
node.next=null;
last.next=node; //最后一个指向node
last=node; //最后一个变为node
}
size++;
}
@Override //重写toString方法,为了输出
public String toString() {
StringBuilder sb=new StringBuilder();
sb.append('[');
//[a,b,c] first=a,last=c
Node temp=first; //定义一个临时节点
while(temp!=null)
{
sb.append(temp.element+",");
temp=temp.next;
}
sb.setCharAt(sb.length()-1, ']');
return sb.toString();
}
public Object get(int index) {
if(index<0||index>size-1) //这里有问题
{
//不合法
throw new RuntimeException("索引不合法!"+index);
}
Node temp=getNode(index);
return temp!=null?temp.element:null;
}
public Node getNode(int index) {
Node temp=first;
if(index<=(size>>1)) //size>>1相当于除以2
{
for(int i=0;i<index;i++)
{
temp=temp.next;
}
}
else
{
temp=last;
for(int i=size-1;i>index;i--)
{
temp=temp.previous;
}
}
return temp;
}
public void remove(int index)
{
Node temp=getNode(index);
if(temp!=null)
{
Node up=temp.previous;
Node down=temp.next;
if(up!=null)
{
up.next=down;
}
if(down!=null)
{
down.previous=up;
}
//被删除的元素是第一个元素时
if(index==0)
{
first=down;
}
//被删除的元素时最后一个元素时
if(index==size-1)
{
last=up;
}
}
}
public void add(int index,Object obj)
{
Node newNode=new Node(obj);
if(index>=0&&index<=size-1)
{
Node temp=getNode(index);
Node up=temp.previous;
if(up!=null)
{
up.next=newNode;
newNode.previous=up;
temp.previous=newNode;
newNode.next=temp;
}
//在开头插入时
if(index==0)
{
first=newNode;
temp.previous=newNode;
newNode.next=temp;
}
}
//在末尾插入时
if(index==size)
{
Node temp=getNode(size-1);
temp.next=newNode;
newNode.previous=temp;
}
}
public static void main(String[] args) {
SxtLinkedList04 list=new SxtLinkedList04();
list.add("a");
list.add("b");
list.add("c");
list.add("a");
list.add("b");
list.add("c");
System.out.println(list.get(0));
list.remove(0);
System.out.println(list);
list.add(0, "韩小狗");
System.out.println(list);
list.add(6, "韩小猪");
System.out.println(list);
}
}
输出结果为:
a
[b,c,a,b,c]
[韩小狗,b,c,a,b,c]
[韩小狗,b,c,a,b,c,韩小猪]