标签:
总结下看LinkedList的相关内容,LinkedList用在对插入,删除操作较频繁的地方
LinkedList 内部是一个双向链表,因此,对插入删除比较高效,但是对于随机访问比ArrayList的速度要低
链表的节点是在LinkedList中定义的一个静态内部类
private static class Node<E> { E item; Node<E> next; Node<E> prev; Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; } }
每个节点有指向它的前驱,和 后继的链表,java中没有指针,但是对象的传递都是地址,可以近似的看作指针
然后就是对链表的一些常用操作了,在内部实现了一些private方法,因为对外的public方法有很多功能是相同的,因此常用操作写成了私有方法
如 首尾添加,首尾删除等
/** * Links e as first element. */ private void linkFirst(E e) { final Node<E> f = first; final Node<E> newNode = new Node<>(null, e, f); first = newNode; if (f == null) last = newNode; else f.prev = newNode; size++; modCount++; } /** * Links e as last element. */ void linkLast(E e) { final Node<E> l = last; final Node<E> newNode = new Node<>(l, e, null); last = newNode; if (l == null) first = newNode; else l.next = newNode; size++; modCount++; } /** * Inserts element e before non-null Node succ. */ void linkBefore(E e, Node<E> succ) { // assert succ != null; final Node<E> pred = succ.prev; final Node<E> newNode = new Node<>(pred, e, succ); succ.prev = newNode; if (pred == null) first = newNode; else pred.next = newNode; size++; modCount++; } /** * Unlinks non-null first node f. */ private E unlinkFirst(Node<E> f) { // assert f == first && f != null; final E element = f.item; final Node<E> next = f.next; f.item = null; f.next = null; // help GC first = next; if (next == null) last = null; else next.prev = null; size--; modCount++; return element; } /** * Unlinks non-null last node l. */ private E unlinkLast(Node<E> l) { // assert l == last && l != null; final E element = l.item; final Node<E> prev = l.prev; l.item = null; l.prev = null; // help GC last = prev; if (prev == null) first = null; else prev.next = null; size--; modCount++; return element; } /** * Unlinks non-null node x. */ E unlink(Node<E> x) { // assert x != null; final E element = x.item; final Node<E> next = x.next; final Node<E> prev = x.prev; if (prev == null) { first = next; } else { prev.next = next; x.prev = null; } if (next == null) { last = prev; } else { next.prev = prev; x.next = null; } x.item = null; size--; modCount++; return element; }
还有就是根据index找到相应的节点,这就是LinkedList对随机访问不太高效的地方,每次想要访问一个中间的元素,先要从两段开始寻找
Node<E> node(int index) { // assert isElementIndex(index); if (index < (size >> 1)) { Node<E> x = first; for (int i = 0; i < index; i++) x = x.next; return x; } else { Node<E> x = last; for (int i = size - 1; i > index; i--) x = x.prev; return x; } }
其余的一些增删操作都是基于这些操作上的。
在LinkedList中还有一个变量modCount,上面的代码中也可以看到,当对LinkedList做add 或者 remove 的时候 这个变量都会自增1,然后这个变量是干什么用的呢?
经过看书以及看代码发现它用在迭代器里面:
private class ListItr implements ListIterator<E> { private Node<E> lastReturned; private Node<E> next; private int nextIndex; private int expectedModCount = modCount;
然后再使用 next remove privious set add等方法的时候都会做一个验证
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
每次对迭代器方法的调用,会验证迭代器中保存的modCount 和 LinkedList 中的modCount 是否相同,如果不同,代表LinkedList被修改了,因此此时的迭代器是失效的,会抛出一个异常
说完这些,说说我的新发现吧,LinkedList实现了Deque接口 而 Deque接口 继承了Queue接口,也就是说LinkedList里面是有队列,和双端队列的方法实现的,不仅如此,它还实现了Stack的方法
可以测试下
@org.junit.Test public void testLinkedListAsStack() { LinkedList<Integer> stack = new LinkedList<>(); stack.push(1); stack.push(2); stack.push(3); stack.push(4); System.out.println("the top of the stack is : " + stack.peek()); System.out.println("the top of the stack is : " + stack.pop()); System.out.println("the size of the stack is : " + stack.size()); } @org.junit.Test public void testLinkedListAsQueue() { LinkedList<Integer> queue = new LinkedList<>(); queue.add(1); queue.add(2); queue.add(3); queue.add(4); System.out.println("the size of queue is : " + queue.size()); System.out.println("the head of queue is : " + queue.peek()); queue.remove(); //poll 方法删除 并返回 head System.out.println("the head of queue is : " + queue.poll()); System.out.println("the head of queue is : " + queue.element()); assertEquals(2, queue.size()); } @org.junit.Test public void testLinkedListAsDeque() { LinkedList<Integer> deque = new LinkedList<>(); //默认从队尾添加 deque.add(1); deque.add(2); deque.add(3); deque.add(4); System.out.println("the size of deque is : " + deque.size()); System.out.println("the head of deque is : " + deque.peekFirst()); System.out.println("the last of deque is : " + deque.peekLast()); deque.pollFirst(); deque.pollLast(); System.out.println("the size of deque is : " + deque.size()); System.out.println("the head of deque is : " + deque.peekFirst()); System.out.println("the last of deque is : " + deque.peekLast()); assertEquals(2,deque.size()); }
经过测试可以发现,LinkedList当作Stack,Deque,Queue来使用是完全没问题的,但是Stack,Deque,Queue里面本身没有多少方法,而且完全可以使用数组来实现,这样更高效(感觉).JDK里面本身是有一个Stack的实现,是继承Vector这个类,Vector类我没用过,也不知道是干嘛的,但是它也是继承AsbtractList 实现List接口,不知道跟ArrayList有什么区别。
因此为就自己用数组实现了Stack,Deque,Queue,当然,实现的挺简单,不一定能够在实际代码中使用,为就当是自己的练手而已
public class MyStack<AnyType> { private static final int DEFAULT_CAPACITY = 10; private int theSize; private AnyType[] theItems; public MyStack() { theItems = (AnyType[]) new Object[DEFAULT_CAPACITY]; } public MyStack(int initLength) { if (initLength <= 0) throw new IllegalArgumentException(); theItems = (AnyType[]) new Object[initLength]; } public AnyType pop() { if (size() <= 0) throw new NoSuchElementException(); return theItems[theSize--]; } public AnyType peek() { if (size() <= 0) throw new NoSuchElementException(); return theItems[theSize-1]; } public void push(AnyType x) { if (size() == theItems.length) ensureCapacity(); theItems[theSize++] = x; } public int size() { return theSize; } public boolean isEmpty() { return theSize == 0; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("["); for (int i = 0; i < theSize; i++) { sb.append(i).append(":").append(theItems[i]).append(","); } sb.deleteCharAt(sb.length() - 1); sb.append("]"); return sb.toString(); } private void ensureCapacity() { int length = theItems.length; AnyType[] oldItems = theItems; theItems = (AnyType[])new Object[length + (length >> 1)]; for (int i = 0; i < length; i++) { theItems[i] = oldItems[i]; } } }
public class MyQueue<AnyType> { private int theSize; private AnyType[] theItems; private int first; private int last; private static final int DEFAULT_CAPACITY = 10; private int length; public MyQueue() { theItems = (AnyType[]) new Object[DEFAULT_CAPACITY]; first = 0; last = 0; length = DEFAULT_CAPACITY; theSize = 0; } public MyQueue(int initLength) { if (initLength <= 0) throw new IllegalArgumentException(); int n = Math.max(initLength,DEFAULT_CAPACITY); theItems = (AnyType[]) new Object[n]; first = 0; last = 0; length = n; theSize = 0; } public boolean add(AnyType x) { if (isOverFlow()) ensureCapacity(); theItems[last] = x; last = (last + 1) % length; theSize++; return true; } public boolean offer(AnyType x) { return add(x); } public void remove() { if (isEmpty()) throw new NoSuchElementException(); theSize--; first++; } public AnyType poll() { if (isEmpty()) throw new NoSuchElementException(); AnyType x = theItems[first++]; theSize--; return x; } public AnyType peek() { if (isEmpty()) throw new NoSuchElementException(); return theItems[first]; } public AnyType element() { return peek(); } public int size() { return theSize; } public boolean isEmpty() { return theSize == 0; } private void ensureCapacity() { AnyType[] oldItems = theItems; int newlength = length + (length >> 1); theItems = (AnyType[]) new Object[newlength]; theItems[0] = oldItems[first++]; int i = 1; while(first != last) { theItems[i++] = oldItems[first]; first = (first + 1)%length; } first = 0; last = length; length = newlength; } private boolean isOverFlow() { return size() == length; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("["); int j = 0; for (int i = first; j < theSize; i = (i+1)%length) { sb.append(j++).append(":").append(theItems[i]).append(","); } sb.deleteCharAt(sb.length() - 1); sb.append("]"); return sb.toString(); } }
public class MyDeque<AnyType> { private int theSize; private AnyType[] theItems; private int first; private int last; private static final int DEFAULT_CAPACITY = 10; private int length; public MyDeque() { theItems = (AnyType[]) new Object[DEFAULT_CAPACITY]; first = 0; last = 0; length = DEFAULT_CAPACITY; theSize = 0; } public MyDeque(int initLength) { if (initLength <= 0) throw new IllegalArgumentException(); int n = Math.max(initLength,DEFAULT_CAPACITY); theItems = (AnyType[]) new Object[n]; first = 0; last = 0; length = n; theSize = 0; } public int size() { return theSize; } public boolean isEmpty() { return theSize == 0; } public void add(AnyType x) { addLast(x); } public void addFirst(AnyType x) { if (isOverFlow()) ensureCapacity(); first = (first -1) < 0?length -1:first -1; theItems[first] = x; theSize++; } public void addLast(AnyType x) { if (isOverFlow()) ensureCapacity(); theItems[last] = x; last = (last + 1) % length; theSize++; } public AnyType remove() { return removeLast(); } public AnyType removeFirst() { if (isEmpty()) throw new NoSuchElementException(); AnyType x = theItems[first]; first = (first + 1)%length; theSize--; return x; } public AnyType removeLast() { if (isEmpty()) throw new NoSuchElementException(); last--; theSize--; return theItems[last]; } public AnyType poll() { return removeLast(); } public AnyType pollFirst() { return removeFirst(); } public AnyType pollLast() { return removeLast(); } public AnyType getFirst() { if (isEmpty()) throw new NoSuchElementException(); return theItems[first]; } public AnyType getLast() { if (isEmpty()) throw new NoSuchElementException(); return theItems[last-1]; } public AnyType peek() { return getFirst(); } public AnyType peekFirst() { return getFirst(); } public AnyType peekLast() { return getLast(); } private void ensureCapacity() { AnyType[] oldItems = theItems; int newlength = length + (length >> 1); theItems = (AnyType[]) new Object[newlength]; theItems[0] = oldItems[first]; first = (first + 1)%length; int i = 1; while(first != last) { theItems[i++] = oldItems[first]; first = (first + 1)%length; } first = 0; last = length; length = newlength; } private boolean isOverFlow() { return size() == length; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("["); int j = 0; for (int i = first; j < theSize; i = (i+1)%length) { sb.append(j++).append(":").append(theItems[i]).append(","); } sb.deleteCharAt(sb.length() - 1); sb.append("]"); return sb.toString(); } }
对这些代码进行了一些测试
@Test public void testMyStack() { MyStack<Integer> stack = new MyStack<>(); stack.push(1); stack.push(2); stack.push(3); stack.push(4); System.out.println("the size of the stack is : " + stack.size()); System.out.println("the top of the stack is : " + stack.peek()); stack.pop(); System.out.println(stack.toString()); assertEquals(3,stack.size()); } @Test public void testMyQueue() { MyQueue<Integer> queue = new MyQueue<>(); queue.add(1); queue.add(2); queue.add(3); queue.add(4); System.out.println(queue.toString()); System.out.println("the size of the queue is : " + queue.size()); System.out.println("the first of the queue is : " + queue.peek()); queue.remove(); System.out.println("the first of the queue is : " + queue.peek()); queue.add(4); queue.add(4); queue.add(4); queue.add(4); queue.add(4); queue.add(4); queue.add(4); queue.add(4); queue.add(4); queue.add(4); System.out.println("the size of the queue is : " + queue.size()); System.out.println("the first of the queue is :" + queue.element()); System.out.println(queue.toString()); assertEquals(13,queue.size()); } @Test public void testMyDeque() { MyDeque<Integer> deque = new MyDeque<>(); deque.add(1); deque.add(2); deque.add(3); deque.add(4); System.out.println("the size of the deque is : " + deque.size()); System.out.println(deque.toString()); deque.addFirst(0); deque.addLast(5); System.out.println("the size of the deque is : " + deque.size()); System.out.println(deque.toString()); deque.addLast(5); deque.addLast(5); deque.addLast(5); deque.addLast(5); deque.addLast(5); deque.addFirst(0); deque.addFirst(0); deque.addFirst(0); deque.addFirst(0); deque.addFirst(0); System.out.println("the size of the deque is : " + deque.size()); System.out.println(deque.toString()); deque.removeFirst(); deque.removeFirst(); deque.removeLast(); deque.removeLast(); System.out.println("the size of the deque is : " + deque.size()); System.out.println(deque.toString()); assertEquals(12, deque.size()); assertEquals(new Integer(0),deque.getFirst()); assertEquals(new Integer(5),deque.getLast()); }
简单的使用应该没什么问题,在并发环境下肯定会出问题,在处理复杂情况下还没测试。
这就是为看LinkedList得到的一些东西了,好像有复制粘贴了好多代码,不过没办法,下次改进
标签:
原文地址:http://www.cnblogs.com/luolei/p/4800164.html