LinkedList底层用双向链表来实现,提高了很多效率。其中有许多值的一看的方法:例如在getNode(int i)方法中,可以先让索引值i与中间值比较,再确定是从前到后遍历还是从后到前遍历,提高了查找效率。在add(int i, Object e)方法中判断索引是否等于size,如果等于size就直接添加,减少了遍历次数,下面是代码。
package com.qianyu.dataStructure.lineTable;
/**
* @author lijing
* @date 2019-03-22-14:26
* @discroption 模拟实现LinkedList
*/
public class LinkedList implements List {
/**
* 内部类,定义结点类
*/
private class Node {
Node pre;//前一个结点
Node next;//后一个结点
Object data;//储存数据
public Node(Node pre, Node next, Object data) {
this.pre = pre;
this.next = next;
this.data = data;
}
}
private Node first;//指向第一个元素
private Node last;//指向最后一个元素
private int size;//LinkedList的长度
/**
* 空构造函数,所有成员均为null
*/
public LinkedList() {
}
@Override
public int size() {
return size;
}
@Override
public Object get(int i) {
//先判断是否越界
checkElementIndex(i, false);
//获取指定索引的结点
return this.getNode(i).data;
}
/**
* 获取指定索引的结点
*
* @param i
* @return
*/
private Node getNode(int i) {
//为了提高效率,可以先让索引值i与中间值比较,再确定是从前到后遍历还是从后到前遍历
if (i < (this.size >> 1)) {
//从前到后遍历
Node p = this.first;
for (int j = 0; j < i; j++) {
p = p.next;
}
return p;
} else {
//从后往前遍历
Node p = this.last;
for (int j = this.size - 1; j > i; j--) {
p = p.pre;
}
return p;
}
}
/**
* 判断索引是否越界
*
* @param i 索引
* @param isAdd 是否是添加元素操作
*/
private void checkElementIndex(int i, boolean isAdd) {
if (!isAdd) {
//如果不是添加操作,不能索引到size
if (i < 0 || i >= this.size) {
throw new MyIndexOutOfBoundsException("无效的索引:" + i);
}
} else {
//如果是添加操作,在索引size出添加相当于在末尾追加
if (i < 0 || i > this.size) {
throw new MyIndexOutOfBoundsException("无效的索引:" + i);
}
}
}
@Override
public boolean isEmpty() {
return size > 0;
}
@Override
public boolean contains(Object e) {
//调用indexOf()函数,如果返回值不是-1就说明在LinkedList中没有找到e
return this.indexOf(e) != -1;
}
@Override
public int indexOf(Object e) {
Node p = this.first;
int index = 0;
//先判断e是否为空
if (e == null) {
while (p != null) {
if (p.data == null) {
return index;
}
index++;
p = p.next;
}
} else {
while (p != null) {
if (p.data.equals(e)) {
return index;
}
index++;
p = p.next;
}
}
return -1;
}
@Override
public void add(int i, Object e) {
//先检查索引是否越界
checkElementIndex(i, true);
//判断如果索引值和size相同,就调用add(Object e)方法,省去了遍历链表的操作,提高了效率
if (i == size) {
this.add(e);
} else {
Node node = getNode(1);//索引为i的结点
Node p = node.pre;//索引为i的结点的前一个结点
Node newNode = new Node(p, node, e);//带插入的新结点
node.pre = newNode;//将原来索引为i的结点的前驱指向新的结点
//判断是否是第一次插入
if (p == null) {
this.first = newNode;
} else {
p.next = newNode;
}
//将size加一
size++;
}
}
@Override
public void add(Object e) {
//1.新建一个节点,将其前驱节点设置为last,后继节点设置为null,数据域设为e
Node newNode = new Node(this.last, null, e);
//2.储存旧尾指针
Node l = this.last;
//3.修改尾指针
this.last = newNode;
//4.判断l是否为空
if (l == null) {
//如果为空就代表是第一次添加,要将头指针指向newNode
this.first = newNode;
} else {
//如果非空就代表不是第一次添加,不用只用将原来的尾指针的next指向newNode即可
l.next = newNode;
}
//5.将长度加一
this.size++;
}
@Override
public Object remove(int i) {
//先检查索引是否越界
checkElementIndex(i, false);
Node p = this.first;
//遍历找到要删除的结点
for (int j = 0; j < i; j++) {
p = p.next;
}
return unlink(p);
}
@Override
public boolean remove(Object e) {
//先判断是否为null
Node p = this.first;
if (e == null) {
while (p != null) {
if (p.data == null) {
//如果找到结点就调用删除结点的方法
unlink(p);
return true;
}
p = p.next;
}
} else {
while (p != null) {
if (p.data.equals(e)) {
//如果找到结点就调用删除结点的方法
unlink(p);
return true;
}
p = p.next;
}
}
return false;
}
/**
* 删除结点p
*
* @param p 要删除的结点
*/
private Object unlink(Node p) {
Node preNode = p.pre;
Node nextNode = p.next;
//先处理要删除结点的前一个结点
if (preNode == null) {
this.first = nextNode;
} else {
preNode.next = nextNode;
p.pre = null;
}
//处理后一个结点
if (nextNode == null) {
this.last = preNode;
} else {
p.next = null;
nextNode.pre = preNode;
}
this.size--;
return p.data;
}
@Override
public Object replace(int i, Object e) {
//先检查索引是否越界
checkElementIndex(i, false);
Node p = this.first;
//遍历找到要替换的结点
for (int j = 0; j < i; j++) {
p = p.next;
}
Object temp = p.data;
p.data = e;
return temp;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("[");
Node p = this.first;
int index = 0;
while (p != null) {
if (index != size - 1) {
sb.append(p.data.toString() + ",");
} else {
sb.append(p.data.toString());
}
p = p.next;
index++;
}
sb.append("]");
return sb.toString();
}
}