更新:以下代码是初学链表后的简单实现,时隔一年再看一下代码。觉得有点惨不忍睹,测试了一下,标准库的链表插入1000万个元素用了10秒钟,以下代码花费的时间远超标准库。因为如果要将元素插入到链表末尾,都需要遍历整个链表。是个O(N)的算法。但实际上,大部分插入都是插入到最后(或是最前)。我们其实只需要维护一个tail节点就能解决这个问题。这样时间复杂度就为O(1)了。
public void add(T val)
{
if(head==null)
{
head = new Node(val);
tail = head;
}
else
{
tail.next = new Node(val);
tail = tail.next;
}
++size;
}
下面是LinkedList的简单实现。
LinkedList的优点: 新项的插入和现有项的删除花费常数时间。(前提是已有变动项的位置,不然还是要在索引上花费O(N) )
LinkedList的缺点: 很难索引现有项,对get操作的调用花费很多时间。
MyLinkedList作为双向链表,get()方法可以从头结点或者尾节点开始。所以最坏的情况下需要遍历 【N/2】 个元素。
代码中changedTimes和itChangedTimes的作用: 如果获取了一个迭代器之后修改它所对应的MyLinkedList对象,则让该迭代器失效。
package list;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* MyLinkedList
* @author earayu
* @param <T>
*/
public class MyLinkedList<T> implements Iterable<T> {
//双向链表的节点
private static class Node<T>{
public T data;
public Node<T> prevNode;
public Node<T> nextNode;
public Node(T data, Node<T> prevNode, Node<T> nextNode) {
this.data = data;
this.prevNode = prevNode;
this.nextNode = nextNode;
}
}
//定义链表的长度,修改次数,头结点和尾节点。
private int size;
private int changedTimes;
private Node<T> firstNode;
private Node<T> lastNode;
/**
* 调用init()方法,初始化链表
*/
public MyLinkedList(){
init();
}
/**
* 初始化MyLinkedList,将长度,被改变次数,头结点和尾节点都初始化。以免发生错误。
*/
private void init(){
size = 0;
changedTimes = 0;
firstNode = new Node<T>(null, null, null);
lastNode = new Node<T>(null, null, null);
firstNode.nextNode = lastNode;
lastNode.prevNode = firstNode;
}
/**
* 将链表清空,修改次数+1
*/
public void clear(){
size = 0;
changedTimes++;
firstNode = new Node<T>(null, null, null);
lastNode = new Node<T>(null, null, null);
firstNode.nextNode = lastNode;
lastNode.prevNode = firstNode;
}
public boolean isEmpty(){
return size() == 0;
}
/**
* 返回当前链表长度(不含头尾节点)
* @return
*/
public int size(){
return size;
}
/**
* 将idx索引出节点的值设置成newData
* @param idx
* @param newData
* @return
*/
public T set(int idx, T newData){
Node<T> p = getNode(idx);
T oldVal = p.data;
p.data = newData;
return oldVal;
}
/**
* 删除制定索引出的节点,索引从0开始
* @param idx
* @return
*/
public T remove(int idx){
return remove(getNode(idx));
}
private T remove(Node<T> p){
p.nextNode.prevNode = p.prevNode;
p.prevNode.nextNode = p.nextNode;
--size;
++changedTimes;
return p.data;
}
/**
* 将data添加到链表尾部。
* @param data
* @return
*/
public boolean add(T data){
add(size, data);
return true;
}
/**
* 将链表添加到idx索引出,索引从0开始
* @param idx
* @param data
* @return
*/
public boolean add(int idx, T data){
if(idx < 0 || idx > size){
throw new IndexOutOfBoundsException();
}
Node<T> beforeNode = firstNode;
for(int i=0;i<size;i++){
beforeNode = beforeNode.nextNode;
}
Node<T> afterNode = beforeNode.nextNode;
beforeNode.nextNode = new Node(data, beforeNode, afterNode);
afterNode.prevNode = beforeNode.nextNode;
++size;
++changedTimes;
return true;
}
/**
* 获取idx索引出的节点的数据,索引从0开始
* @param idx
* @return
*/
public T get(int idx){
return getNode(idx).data;
}
/**
* 获取idx索引出的节点
* @param idx
* @return
*/
private Node<T> getNode(int idx){
if(idx < 0 || idx >= size){
throw new IndexOutOfBoundsException();
}
Node<T> p;
if(idx < size/2){
p = firstNode.nextNode;
for(int i=0; i<idx; i++){
p = p.nextNode;
}
}else{
p = lastNode;
for(int i=size; i>idx; i--){
p = p.prevNode;
}
}
return p;
}
/**
* 返回一个迭代器
*/
@Override
public Iterator<T> iterator() {
return new LinkedListIterator();
}
/**
*
* @author earayu
*
*/
private class LinkedListIterator implements Iterator<T>{
//迭代器的当前指向节点,修改次数,可否移除
private Node<T> currentNode = firstNode.nextNode;
private int itChangedTimes = changedTimes;
private boolean removable = false;
/**
* 返回是否有下一个节点(不包含尾节点)。
*/
@Override
public boolean hasNext() {
return currentNode != lastNode;
}
/**
* 返回当前指向节点的下一个节点的值,并将指向的节点后移一位。
*/
@Override
public T next() {
if(changedTimes != itChangedTimes){
throw new ConcurrentModificationException();
}
if(!hasNext()){
throw new NoSuchElementException();
}
T nextItem = currentNode.data;
currentNode = currentNode.nextNode;
removable = true;
return nextItem;
}
/**
* 调用外部类的remove方法,移除上一个访问过的节点。即,当前指向节点的前一个节点。
*/
public void remove(){
if(changedTimes != itChangedTimes){
throw new ConcurrentModificationException();
}
if(!removable){
throw new IllegalStateException();
}
MyLinkedList.this.remove(currentNode.prevNode);
removable = false;
++itChangedTimes;
}
}
}