zoukankan      html  css  js  c++  java
  • 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得到的一些东西了,好像有复制粘贴了好多代码,不过没办法,下次改进

  • 相关阅读:
    【BZOJ3506】[CQOI2014] 排序机械臂(Splay)
    2019年1月训练记录(1.01~1.31)
    再学点分治——动态点分治
    【洛谷5113】Sabbat of the witch(毒瘤分块)
    【洛谷1501】[国家集训队] Tree II(LCT维护懒惰标记)
    初学后缀自动机
    【洛谷3950】部落冲突(LCT维护连通性)
    【BZOJ2049】[SDOI2008] Cave 洞穴勘测(LCT维护连通性)
    LCT入门
    【BZOJ4650】[NOI2016] 优秀的拆分(后缀数组)
  • 原文地址:https://www.cnblogs.com/luolei/p/4800164.html
Copyright © 2011-2022 走看看