LinkedList

总结下看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得到的一些东西了,好像有复制粘贴了好多代码,不过没办法,下次改进

原文地址:https://www.cnblogs.com/luolei/p/4800164.html