手写LinkedList实现(基于双链表)
上次我通过单链表实现了List接口,但是单链表的结构只可以允许从前到后遍历获取数据显的不是那么的方便,在java中一般使用双向链表来获取存储数据,双向链表中的每个节点除了要保存它的下一个节点对象的引用以外还会保存一个它前一个节点对象的引用,这样就可以实现双向查找数据。
首先定义个新的节点接口:
public class DLNodel implements Node{ private Object element; private DLNodel next; private DLNodel pre; @Override public Object getData() { return element; } @Override public void setDate(Object data) { element = data; } public DLNodel getNext() { return next; } public void setNext(DLNodel next) { this.next = next; } public DLNodel getPre() { return pre; } public void setPre(DLNodel pre) { this.pre = pre; } }
在这个接口里面相比单链表节点添加了pre属性用来保存前一个节点对象的引用。List接口的实现:
public class LinkedDList<E> implements List<E>{ private DLNodel head; //链表头 private int size; private DLNodel tail; //链表尾 public LinkedDList() { head = new DLNodel(); tail = new DLNodel(); head.setNext(tail); tail.setPre(head); } private DLNodel getNode(int index){ DLNodel node = null; if(index>(size>>1)) { //从后向前找 int lastindex = size - 1 - index; node = tail; for (; lastindex > 0; lastindex--) { node = node.getPre(); } }else{ node = head; for(;index>0;index--){ node = node.getNext(); } } return node; } @Override public int size() { return size; } @Override public boolean isEmpty() { return size==0; } @Override public boolean contains(Object o) { DLNodel node = head; if(head!=null){ while (head!=null){ if(head.getData().equals(o)){ return true; } head = head.getNext(); } } return false; } @Override public boolean add(E e) { if(size==0){ head.setDate(e); size++; return true; }else if(size==1){ tail.setDate(e); size++; return true; }else{ DLNodel oldtail = tail; DLNodel newtail = new DLNodel(); newtail.setDate(e); oldtail.setNext(newtail); newtail.setPre(oldtail); tail = newtail; size++; return true; } } @Override public boolean remove(Object o) { int index = indexOf(o); E e = remove(index); return e!=null; } @Override public E get(int index) { if(index<0||index>size-1){ return null; } DLNodel node = getNode(index); if(node!=null){ return (E)node.getData(); }else{ return null; } } @Override public E set(int index, E element) { if(index<0||index>size-1){ return null; } E e = null; DLNodel node = getNode(index); if(node!=null){ e = (E)node.getData(); node.setDate(element); } return e; } @Override public void add(int index, E element) { if(index<0||index>size-1){ throw new RuntimeException("索引越界"); } if(index==size){ add(element); }else{ if(index==0){ DLNodel oldnode = head; DLNodel newtail = new DLNodel(); oldnode.setPre(newtail); newtail.setDate(element); newtail.setNext(oldnode); head = newtail; size++; }else{ DLNodel node = getNode(index); DLNodel newnode = new DLNodel(); newnode.setDate(element); newnode.setPre(node); newnode.setNext(node.getNext()); node.getNext().setPre(newnode); node.setNext(newnode); size++; } } } @Override public E remove(int index) { if(index<0||index>size-1){ throw new RuntimeException("索引越界"); } if(index==0){ //移除头 E result = (E)head.getData(); head.getNext().setPre(null); head = head.getNext(); size--; return result; }else if(index==size-1){ //移除尾 E result = (E)tail.getData(); tail.getPre().setNext(null); tail = tail.getPre(); size--; return result; }else{ DLNodel node = getNode(index); E result = (E)node.getData(); node.getPre().setNext(node.getNext()); node.getNext().setPre(node.getPre()); size--; return result; } } @Override public int indexOf(Object o) { DLNodel p = head; int index = 0; while (p.getNext()!=null){ if(p.getData().equals(o)){ return index; } p = p.getNext(); index++; } return -1; } @Override public int lastIndexOf(Object o) { DLNodel p = head; int index = size-1; while (p.getNext()!=null){ if(p.getData().equals(o)){ return index; } p = p.getNext(); index--; } return -1; } }
双向链表因为可以双向遍历查找数据所以查询数据的效率要比单向链表高,但是因为双向链表的节点之间的关系要比单向链表复杂,所以在添加和删除时的效率不如单向列表。总体来讲双向链表的性能要比单向链表高。