• ArrayList和LinkedList 内部结构分析(二)


      前面一篇博客,写了关于ArrayList的源码分析,发现对于ArrayList,其内部是数组结构,在查询和遍历的时候效率较高,而在增加元素、删除的时候效率就比较慢了。

      那么对于我们常用的另外一个List,LinkedList内部结构是什么样子的呢?

      直接开始上代码:

       一、LinkedList内部结构:

    public class LinkedList<E>
        extends AbstractSequentialList<E>
        implements List<E>, Deque<E>, Cloneable, java.io.Serializable
    {
       //起始端值
        transient Node<E> first;
    
        //尾端值
        transient Node<E> last;
    
       //构造函数
        public LinkedList() {
        }
    
       //node 一个匿名内部类,这其实就是LinkList里面的最小元素
       //三个属性  当前对象  前面一个对象  后面一个对象
       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;
            }
        }
    
    }

      由一个个的Node节点连接起来,Node节点通过其prev和next属性,在寻找前面借点和后面借点是什么,头节点和尾部节点分别是first和last

    二、对ListLinked内部元素的操作

         1、获取元素 

             (1)getFirst()/getLast() 

      transient Node<E> first;
    
      transient Node<E> last;
    
       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;
            }
        }
    //获取第一个元素
    public E getFirst() {
             //第一个元素赋值给f
            final Node<E> f = first;
             //判断非空
            if (f == null)
                throw new NoSuchElementException();
            //返回元素
            return f.item;
        }
    
    public E getLast() {
            final Node<E> l = last;
            if (l == null)
                throw new NoSuchElementException();
            return l.item;
        }

      查询获取首个元素和最后一个元素,直接获取first和last,效率很高。

        (2)get(index)

    //根据下角标获取元素
    public E get(int index) {
           //检查下角标范围
            checkElementIndex(index);
           //执行node(index)得到下角标为index的元素
            return node(index).item;
        }
    
    
    Node<E> node(int index) {
            // assert isElementIndex(index);
            //判断index在前面一半还是后面一半
            if (index < (size >> 1)) {
            //从第一个值开始遍历
                Node<E> x = first;
                for (int i = 0; i < index; i++)
                    x = x.next;
                //遍历结束,到达第index个值
                return x;
            } else {
             //从last开始遍历
                Node<E> x = last;
                for (int i = size - 1; i > index; i--)
                    x = x.prev;
                return x;
            }
        }

      get(index),需要从first或者last一一遍历,并且需要计数,知道到达需要拿到index的元素,所以效率很低。

    2、设置元素值

    //设置下角标index值
    public E set(int index, E element) {
          //检查index范围
            checkElementIndex(index);
         //获取index
            Node<E> x = node(index);
           //记录原来的值
            E oldVal = x.item;
          //设置新值
            x.item = element;
          //返回原来的值
            return oldVal;
        }
    
    //通过遍历获取index下角标元素
    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;
            }
        }

     set(index),需要先取得index下角标的元素,然后再将Node的item属性设置为新值,效率比较低下。

    3、add(index,element)

    public void add(int index, E element) {
          //检查index位置
            checkPositionIndex(index);
    
            if (index == size)
            //放在最后
                linkLast(element);
            else
            //其余,传入index位置元素和新元素
                linkBefore(element, node(index));
        }
    
            //传入index位置元素和新元素,
    void linkBefore(E e, Node<E> succ) {
            // assert succ != null;
            //愿index元素前面的Node
            final Node<E> pred = succ.prev;
            //新元素  前面的值为原index元素前面的值,后面为原index元素
            final Node<E> newNode = new Node<>(pred, e, succ);
           //原index元素,前面值更新为插入值
            succ.prev = newNode;
            if (pred == null)
             //判断新插入元素,前面值是否为空,如果是空则说明是第一个元素
                first = newNode;
            else
                //设置前面值的对应next为新增值
                pred.next = newNode;
          //添加大小
            size++;
        //修改次数更新
            modCount++;
        }

    新增元素,还是比较方便的,最多只要动三个元素调整pre和next就可以,还是比较效率的。

    其余的,比如删除等等,跟add类似,效率都比较高。

  • 相关阅读:
    C# 停止或开启服务
    微软企业库 缓存
    C# 获取图片一部分
    [转载]MVC3在win 2003虚拟主机上的调试
    mongodb查询的语法
    Mongodb亿级数据量的性能测试比较完整收藏一下
    正则验证数字
    收到了Gmail的Beta测试邀请
    C#新手经验点滴
    Windows消息机制
  • 原文地址:https://www.cnblogs.com/guoliangxie/p/5319772.html
Copyright © 2020-2023  润新知