• 8、集合--LinkedList的测试以及相关方法的源码分析


    LinkedList测试

        public static void main(String[] args) {
    
            List list = new LinkedList();
    
            //添加元素
            list.add(1);
            list.add(2);
            //在指定位置添加元素
            list.add(0,0);
            System.out.println("list中的元素:" + list);
    
            //对象首次出现的位置
            System.out.println("0对象首先出现的位置:"+list.indexOf(0));
            //对象最后出现的位置
            System.out.println("1对象首先出现的位置:"+list.lastIndexOf(1));
    
            //for循环遍历
            for (int i = 0;i < list.size();i++){
                System.out.println("for循环遍历:" + list.get(i));
            }
    
            //返回列表的长度
            System.out.println("长度:" + list.size());
    
            //迭代器遍历
            Iterator it = list.iterator();
            while (it.hasNext()){
                System.out.println("迭代器遍历 :" + it.next() );
            }
    
            //将对象添加在集合的开头
            ((LinkedList) list).addFirst("a");
            //将对象添加在集合的末尾
            ((LinkedList) list).addLast("b");
            System.out.println("list中的元素:" + list);
    
            //修改指定位置的对象
            list.set(0,"c");
            System.out.println("修改0位置的对象值为c:" + list);
    
            //获取集合中开头的元素
            System.out.println("开头的元素:" + ((LinkedList) list).getFirst());
    
            //获取集合中末尾的元素
            System.out.println("末尾的元素:" + ((LinkedList) list).getLast());
    
            //删除列表开头的元素
            ((LinkedList) list).removeFirst();//删除的是a
            //删除列表末尾的元素
            ((LinkedList) list).removeLast();//删除的是b
            System.out.println("删除开头和结尾之后的列表:" + list);
    
            //删除指定索引的位置
            list.remove(0);
            System.out.println("删除指定索引位置的列表:" + list);
    
            //判断列表是否为空
            System.out.println("是否为空:" +  list.isEmpty());
    
            //是否包含对象1
            System.out.println("是否包含对象1 :" + list.contains(1));
    
            //清空集合
            list.clear();
        }

    相关底层的方法实现:

    1、new对象之后List list = new LinkedList();

    可以看作是一个双向的链表每个节点都有first节点last节点

    方法都是通过移动节点指向来实现的

      //集合元素数量
      transient int size = 0;   //链表头节点 transient Node<E> first;   //链表为节点 transient Node<E> last;
      protected transient int modCount = 0;
     public 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;
       }
      }

    2、add(E e)方法

        public boolean add(E e) {
            linkLast(e);
            return true;
        }

    调用linkLast()方法:

     void linkLast(E e) {q
            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++;
        }
    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;
            }
        }

    3、indexOf(Object o):o元素首先出现的位置

    首先定义一个索引值index=0;用于返回元素第一次出现的位置

    判断传入的对象o是否为null

    ①、如果为null

      使用for循环进行遍历

      从起始节点first开始

      然后判断Node类的item属性是否等于null 

      如果等于null则返回index

      否则index++

    ②、如果传入的对象o不为null

      直接进行遍历链表

      使用equals()方法进行判断元素o和item属性值是否相同

      如果等于则返回index  

      否则index ++

    ③、如果这个对象不存在

      此时直接返回-1

    此时在循环中使用x=x.next向下循环遍历

    public int indexOf(Object o) {
            int index = 0;
            if (o == null) {//如果目标对象是null
                for (Node<E> x = first; x != null; x = x.next) {
                    if (x.item == null)
                        return index;
                    index++;
                }
            } else {//遍历链表
                for (Node<E> x = first; x != null; x = x.next) {
                    if (o.equals(x.item))
                        return index;
                    index++;
                }
            }
            return -1;
        }

    4、lastIndexOf(Object o):判断元素最后一次出现的所以位置

    此时定义index所引值大小size,从链表的最后面开始遍历,即尾节点开始遍历

    判断传入的元素o是否为空:

    ①、如果传入的元素o为null

      此时循环中定义的Node对象x=last即尾节点

      首先将index--

      在去判断x.item 是否等于null,然后返回index

      遍历是使用x = x.prev只想前置节点

    ②、如果不为null

      则首先让index--

      然后使用equals()方法进行数据的对比

      如果比对相同则返回index

    ③、如果这个元素o不存在则返回-1

    public int lastIndexOf(Object o) {
            int index = size;
            if (o == null) {
                for (Node<E> x = last; x != null; x = x.prev) {
                    index--;
                    if (x.item == null)
                        return index;
                }
            } else {
                for (Node<E> x = last; x != null; x = x.prev) {
                    index--;
                    if (o.equals(x.item))
                        return index;
                }
            }
            return -1;
        }

    5、size()方法:返回链表的长度

    此时直接返回size的值

        public int size() {
            return size;
        }

    6、get(int index)方法:获取指定索引位置的值

    checkElementIndex(index)方法检测index是否越界

    具体的实现在isElementIndex(int index)进行返回

        public E get(int index) {
            checkElementIndex(index);
            return node(index).item;
        }
    private void checkElementIndex(int index) {
            if (!isElementIndex(index))
                throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        }
        private boolean isElementIndex(int index) {
            return index >= 0 && index < size;
        }
    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;
            }
        }

    最后返回node(int index)方法

    进行查找指定位置的Node类的实例

    7、addFirst(E e):在链表的第一个位置添加元素o

        public void addFirst(E e) {
            linkFirst(e);
        }
     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++;
        }

    首先定义一个Node的实例 f 并且只想first(即只想首链)

    在新建一个Node的实现newNode,用于创建新的实例,且next指向f

    再把newNode的值赋值给first(首节点)

    判断此时的f是否为空(首节点是否为null)

    ①、如果为空:则将newNode赋值给last

    ②、不为空:则将原首节点的prev指向新的节点newNode

    8、addLast(E e):将对象添加在集合的末尾

    public void addLast(E e) {
            linkLast(e);
        }
    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++;
        }

    首相定义Node的实例 l 指向尾节点

    在定义一个新的实例newNode,且prev属性指向l(即last尾节点)

    此时在将newNode赋值非尾节点last

    判断l是否为空:尾节点是否为空

    ①、如果为空:则把newNode赋值非first首节点

    ②、不为空:则将尾节点l的next属性指向新建的节点newNode

    9、set(int index,E element):修改指定位置的元素值

    ①、调用checkElementIndex()方法去检验index是否合法

    ②、调用node(int index)方法去找指定位置上的node节点

    ③、将旧值保存在oldValue上,在进行将要改的值element赋值给找到的节点

    ④、返回旧值oldValue

    public E set(int index, E element) {
            checkElementIndex(index);
            Node<E> x = node(index);
            E oldVal = x.item;
            x.item = element;
            return oldVal;
        }
        private void checkElementIndex(int index) {
            if (!isElementIndex(index))
                throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        }
    Node<E> node(int 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;
            }
        }

    10、getFirst():获取集合中开头的元素

    public E getFirst() {
            final Node<E> f = first;
            if (f == null)
                throw new NoSuchElementException();
            return f.item;
        }

    首相定义一个Node实例f指向链表的首节点first

    在进行判断首节点是否为null

    最后返回首节点的item属性值

    11、getLast():获取集合中末尾的元素

    public E getLast() {
            final Node<E> l = last;
            if (l == null)
                throw new NoSuchElementException();
            return l.item;
        }

    与getFirst()方法类似

    12、removeFirst():删除列表开头的元素

        public E removeFirst() {
            final Node<E> f = first;
            if (f == null)
                throw new NoSuchElementException();
            return unlinkFirst(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;
        }

    首先定义一个Node实例f指向首节点first

    在进行判断f是否为空

    最后调用unlinkFirst()方法

    定义一个值element用于保存首节点中的值(f.item)

    在定义一个Node的实例next作为临时变量来保存第二个节点(first.next)

    然后将first的item和next值均赋值为null

    最后将first重新指向next的临时变量

    判断临时变量next是否为空:

    为空:将尾节点置为null

    不为空:将临时节点的prev的属性值置为空

    13、removeLast():删除列表末尾的元素

     public E removeLast() {
            final Node<E> l = last;
            if (l == null)
                throw new NoSuchElementException();
            return unlinkLast(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;
        }

    和removeFirst()类似

    14、remove(int index):删除指定索引的位置

     public E remove(int index) {
            checkElementIndex(index);
            return unlink(node(index));
        }
     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;
        }

    思想:

    找到指定位置上的节点之后假设为A

    A的前置节点为A1,A的后置节点为A2

    此时将A1.next 指向 A2

    同时将A节点置为null

    15、remove(Object o):删除元素o

    public boolean remove(Object o) {
            if (o == null) {
                for (Node<E> x = first; x != null; x = x.next) {
                    if (x.item == null) {
                        unlink(x);
                        return true;
                    }
                }
            } else {
                for (Node<E> x = first; x != null; x = x.next) {
                    if (o.equals(x.item)) {
                        unlink(x);
                        return true;
                    }
                }
            }
            return false;
        }
     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;
        }

    思想:首先判断该元素o是否为空在进行遍历链表

    unlink()方法的思想同上

    16、isEmpty():集合是否为空

        public boolean isEmpty() {
            return size() == 0;
        }

    即判断size()是否为0

    17、contaions(Object o):判断集合中是否包含元素o

        public boolean contains(Object o) {
            return indexOf(o) != -1;
        }
    public int indexOf(Object o) {
            int index = 0;
            if (o == null) {
                for (Node<E> x = first; x != null; x = x.next) {
                    if (x.item == null)
                        return index;
                    index++;
                }
            } else {
                for (Node<E> x = first; x != null; x = x.next) {
                    if (o.equals(x.item))
                        return index;
                    index++;
                }
            }
            return -1;
        }

    此时的思想是:

    查找元素第一次出现的位置进行返回

    18、clear():清空集合

    public void clear() {
    
            for (Node<E> x = first; x != null; ) {
                Node<E> next = x.next;
                x.item = null;
                x.next = null;
                x.prev = null;
                x = next;
            }
            first = last = null;
            size = 0;
            modCount++;
        }

    即从头遍历集合

    同时将item、next、prev三个属性值都置为0

    最后将size的值置为0

    以上是对LinkedList底层一些方法实现的分析

    更多的实现可以看底层源码

  • 相关阅读:
    HDU 5671 矩阵
    HDU 5670
    UVA 11995 STL 使用
    VK Cup 2016
    字段定义
    apache用户
    apache
    使用第三方登录
    setex()
    如果客户端禁用了cookie,如何实现session
  • 原文地址:https://www.cnblogs.com/Mrchengs/p/10847242.html
Copyright © 2020-2023  润新知