• Java集合之LinkedList常用方法解析


      最近正准备回顾一下Java,所以在此做一些记录。

    1.LinkedList使用的是链表结构,先看一下节点的定义

     1  /**
     2      *
     3      * 连接的节点
     4      */
     5     private static class Node<E> {
     6         //保存的数据
     7         E item;
     8         //后置节点
     9         Node<E> next;
    10         //前置节点
    11         Node<E> prev;
    12         //构造方法
    13         Node(Node<E> prev, E element, Node<E> next) {
    14             this.item = element;
    15             this.next = next;
    16             this.prev = prev;
    17         }
    18     }
    View Code

    2.add(E e) 添加一个元素

     1  /**
     2      * 添加一个元素
     3      *
     4      * @param e 添加的元素
     5      */
     6     public boolean add(E e) {
     7         //调用添加到末尾
     8         linkLast(e);
     9         return true;
    10     }
    11 
    12 
    13 /**
    14      * 添加一个元素到链表的尾部
    15      */
    16     void linkLast(E e) {
    17         //获取链表的尾部节点
    18         final Node<E> l = last;
    19         //创建新的节点,前置节点为last,后置节点为空
    20         final Node<E> newNode = new Node<>(l, e, null);
    21         //重新设置尾部节点
    22         last = newNode;
    23         //如果之前的尾部节点为空,说明之前没有元素
    24         if (l == null)
    25             //设置头节点也为空
    26             first = newNode;
    27         else
    28             //如果之前的尾部节点不为空,则为之前的尾部节点追加后置节点
    29             l.next = newNode;
    30         //数量加一
    31         size++;
    32         modCount++;
    33     }   
    34  
    View Code

    3.add(int index, E element)  添加一个元素到指定位置

     1  /**
     2      * 添加一个元素到指定位置
     3      *
     4      * @param index 指定的位置
     5      * @param element 添加的元素
     6      */
     7     public void add(int index, E element) {
     8         //检查指定的位置是否越界
     9         checkPositionIndex(index);
    10         //如果插入的是尾部,直接调用插入尾部的方法
    11         if (index == size)
    12             linkLast(element);
    13         else
    14             //需要先找到指定位置的节点
    15             // 插入到该节点之前
    16             linkBefore(element, node(index));
    17     }
    18 
    19  //检查是否越界
    20     private void checkPositionIndex(int index) {
    21         if (!isPositionIndex(index))
    22             throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    23     }
    24 
    25   private boolean isPositionIndex(int index) {
    26         return index >= 0 && index <= size;
    27     }
    28 
    29 /**
    30      * 查询指定位置的节点
    31      */
    32     Node<E> node(int index) {
    33         // 类似于二分查询
    34         // 如果位置小于集合大小的一半,从头节点开始遍历
    35         // 否则从尾部节点开始遍历
    36 
    37         if (index < (size >> 1)) {
    38             Node<E> x = first;
    39             for (int i = 0; i < index; i++)
    40                 x = x.next;
    41             return x;
    42         } else {
    43             Node<E> x = last;
    44             for (int i = size - 1; i > index; i--)
    45                 x = x.prev;
    46             return x;
    47         }
    48     }
    49 
    50   /**
    51      * 在某个节点之前进行插入
    52      */
    53     void linkBefore(E e, Node<E> succ) {
    54         // 获取当前节点的前置节点
    55         final Node<E> pred = succ.prev;
    56         //保存创建的新节点
    57         final Node<E> newNode = new Node<>(pred, e, succ);
    58         //将新节点设置为当前位置节点的前置节点
    59         succ.prev = newNode;
    60         //如果当前位置节点的前置节点为空,则说明当前位置为头节点
    61         if (pred == null)
    62             //重新设置头节点
    63             first = newNode;
    64         else
    65             //将当前位置节点的前置节点的后置节点设置为新节点
    66             pred.next = newNode;
    67         //数量增加
    68         size++;
    69         modCount++;
    70     }
    View Code

    4.get(int index) 获取指定位置的元素

     1 /**
     2      * 获取指定位置的元素
     3      *
     4      * @param index 指定的位置
     5      * @return 查询到的元素
     6      */
     7     public E get(int index) {
     8         //判断是否越界
     9         checkElementIndex(index);
    10         //查询到节点的值
    11         return node(index).item;
    12     }
    13 
    14 //检查是否越界
    15     private void checkElementIndex(int index) {
    16         if (!isElementIndex(index))
    17             throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    18     }
    19 
    20   private boolean isElementIndex(int index) {
    21         return index >= 0 && index < size;
    22     }
    23 
    24  /**
    25      * 查询指定位置的节点
    26      */
    27     Node<E> node(int index) {
    28         // 类似于二分查询
    29         // 如果位置小于集合大小的一半,从头节点开始遍历
    30         // 否则从尾部节点开始遍历
    31 
    32         if (index < (size >> 1)) {
    33             Node<E> x = first;
    34             for (int i = 0; i < index; i++)
    35                 x = x.next;
    36             return x;
    37         } else {
    38             Node<E> x = last;
    39             for (int i = size - 1; i > index; i--)
    40                 x = x.prev;
    41             return x;
    42         }
    43     }
    View Code

    5.remove(Object o) 删除指定的元素

     1  /**
     2      * 删除指定的元素
     3      *
     4      * @param o 需要删除的元素
     5      */
     6     public boolean remove(Object o) {
     7         //判断需要删除的元素是否为空
     8         if (o == null) {
     9             //如果为空,从头节点开始遍历
    10             for (Node<E> x = first; x != null; x = x.next) {
    11                 if (x.item == null) {
    12                     //删除节点
    13                     unlink(x);
    14                     return true;
    15                 }
    16             }
    17         } else {
    18             //删除的元素不为为空,也是头节点开始遍历
    19             for (Node<E> x = first; x != null; x = x.next) {
    20                 if (o.equals(x.item)) {
    21                     //删除节点
    22                     unlink(x);
    23                     return true;
    24                 }
    25             }
    26         }
    27         return false;
    28     }
    29 
    30  /**
    31      * 删除一个节点
    32      */
    33     E unlink(Node<E> x) {
    34         // 获取删除节点的值
    35         final E element = x.item;
    36         //获取删除节点的后置节点
    37         final Node<E> next = x.next;
    38         //获取删除节点的前置节点
    39         final Node<E> prev = x.prev;
    40 
    41         //判断前置节点是否为空
    42         if (prev == null) {
    43             //为空则其后置节点晋升为头节点
    44             first = next;
    45         } else {
    46             //修改前置节点的后置节点
    47             prev.next = next;
    48             //将删除节点的前置节点置空
    49             x.prev = null;
    50         }
    51 
    52         //判断后置节点是否为空
    53         if (next == null) {
    54             //后置节点为空,则其前置节点修改为尾部节点
    55             last = prev;
    56         } else {
    57             //后置节点的前置节点修改
    58             next.prev = prev;
    59             //删除节点的后置节点置空
    60             x.next = null;
    61         }
    62         //删除节点的值设置为空
    63         x.item = null;
    64         //数量减1
    65         size--;
    66         modCount++;
    67         return element;
    68     }
    View Code

    6.set(int index, E element) 在指定位置节点设置值

     1  /**
     2      * 在指定位置节点设置值
     3      *
     4      * @param index 指定的位置
     5      * @param element 元素值
     6      */
     7     public E set(int index, E element) {
     8         //检查位置是否越界
     9         checkElementIndex(index);
    10         //查询到当前位置的节点
    11         Node<E> x = node(index);
    12         //获取旧值
    13         E oldVal = x.item;
    14         //设置新值
    15         x.item = element;
    16         //返回旧值
    17         return oldVal;
    18     }
    19 
    20  /**
    21      * 查询指定位置的节点
    22      */
    23     Node<E> node(int index) {
    24         // 类似于二分查询
    25         // 如果位置小于集合大小的一半,从头节点开始遍历
    26         // 否则从尾部节点开始遍历
    27 
    28         if (index < (size >> 1)) {
    29             Node<E> x = first;
    30             for (int i = 0; i < index; i++)
    31                 x = x.next;
    32             return x;
    33         } else {
    34             Node<E> x = last;
    35             for (int i = size - 1; i > index; i--)
    36                 x = x.prev;
    37             return x;
    38         }
    39     }
    View Code

    7.indexOf(Object o) 查询指定元素值所在位置,lastIndexOf也一样,只是从尾部节点开始查询

     1     /**
     2      * 查询指定元素值所在位置
     3      *
     4      * @param o 元素值
     5      */
     6     public int indexOf(Object o) {
     7         int index = 0;
     8         //判断查询的值是否为null
     9         if (o == null) {
    10             //从头节点开始遍历查询
    11             for (Node<E> x = first; x != null; x = x.next) {
    12                 if (x.item == null)
    13                     return index;
    14                 index++;
    15             }
    16         } else {
    17             //从头节点开始遍历查询
    18             for (Node<E> x = first; x != null; x = x.next) {
    19                 if (o.equals(x.item))
    20                     return index;
    21                 index++;
    22             }
    23         }
    24         return -1;
    25     }
    View Code

    8.peek() 获取头节点的元素值,但不删除头节点

       poll() 获取头节点的元素值,并删除头节点

     pop() 获取头节点的元素值,并删除头节点,头节点为空则抛出异常

     1     /**
     2      * 获取头节点的元素值,但不删除头节点
     3      *
     4      */
     5     public E peek() {
     6         final Node<E> f = first;
     7         return (f == null) ? null : f.item;
     8     }
     9 
    10  /**
    11      * 获取头节点的元素值,并删除头节点
    12      */
    13     public E poll() {
    14         final Node<E> f = first;
    15         return (f == null) ? null : unlinkFirst(f);
    16     }
    17 
    18  /**
    19      * 删除头节点
    20      */
    21     private E unlinkFirst(Node<E> f) {
    22         //获取节点的值
    23         final E element = f.item;
    24         //获取到后置节点
    25         final Node<E> next = f.next;
    26         //清空节点的值和后置节点
    27         f.item = null;
    28         f.next = null;
    29         //重新设置头节点
    30         first = next;
    31         //判断后置节点是否为null
    32         if (next == null)
    33             //说明只有这么一个节点,尾部节点设置null
    34             last = null;
    35         else
    36             //已经设置为头节点了,所以清空前置节点
    37             next.prev = null;
    38         //数量减1
    39         size--;
    40         modCount++;
    41         return element;
    42     }
    43 
    44 /**
    45      * 获取头节点的元素值,并删除头节点
    46      */
    47     public E pop() {
    48         return removeFirst();
    49     }
    50 
    51  /**
    52      * 删除头节点
    53      */
    54     public E removeFirst() {
    55         final Node<E> f = first;
    56         //头节点为空则抛出异常
    57         if (f == null)
    58             throw new NoSuchElementException();
    59         //删除头节点
    60         return unlinkFirst(f);
    61     }
    View Code

    9.offer(E e)  添加新元素到末尾

      push(E e) 添加新元素到头节点

     1 /**
     2      * 添加新元素到尾部
     3      *
     4      * @param e 新元素
     5      */
     6     public boolean offer(E e) {
     7         return add(e);
     8     }
     9 
    10 
    11     /**
    12      * 添加一个元素
    13      *
    14      * @param e 添加的元素
    15      */
    16     public boolean add(E e) {
    17         //调用添加到末尾
    18         linkLast(e);
    19         return true;
    20     }
    21 
    22   /**
    23      * 添加一个元素到链表的尾部
    24      */
    25     void linkLast(E e) {
    26         //获取链表的尾部节点
    27         final Node<E> l = last;
    28         //创建新的节点,前置节点为last,后置节点为空
    29         final Node<E> newNode = new Node<>(l, e, null);
    30         //重新设置尾部节点
    31         last = newNode;
    32         //如果之前的尾部节点为空,说明之前没有元素
    33         if (l == null)
    34             //设置头节点也为空
    35             first = newNode;
    36         else
    37             //如果之前的尾部节点不为空,则为之前的尾部节点追加后置节点
    38             l.next = newNode;
    39         //数量加一
    40         size++;
    41         modCount++;
    42     }
    43 
    44 /**
    45      * 添加新元素到头节点
    46      *
    47      * @param e the element to push
    48      * @since 1.6
    49      */
    50     public void push(E e) {
    51         addFirst(e);
    52     }
    53 
    54 
    55     /**
    56      * 添加元素到头节点
    57      *
    58      * @param e 新增的元素
    59      */
    60     public void addFirst(E e) {
    61         //添加元素到头节点
    62         linkFirst(e);
    63     }
    64 
    65  /**
    66      * 添加元素到头节点
    67      */
    68     private void linkFirst(E e) {
    69         //获取当前的头节点
    70         final Node<E> f = first;
    71         //创建新节点
    72         final Node<E> newNode = new Node<>(null, e, f);
    73         //设置新节点为头节点
    74         first = newNode;
    75         //判断头节点是否为空
    76         if (f == null)
    77             //头节点为空,说明之前没有节点,新节点同时为尾部节点
    78             last = newNode;
    79         else
    80             //头节点不为空,则头节点的前置节点指向新节点
    81             f.prev = newNode;
    82         //数量加一
    83         size++;
    84         modCount++;
    85     }
    View Code

     总结一下

    1.LinkedList作为双向链表,维护了头尾节点,头尾节点的插入比较方便,中间数据的插入需要遍历查询再做插入

    2.查询指定位置时,使用了一次二分,最多需要遍历一半的节点

  • 相关阅读:
    提高关键词排名的28个优化技巧
    Web存储机制—sessionStorage,localStorage使用方法
    19个JavaScript简化编码小技巧
    禁止浏览器滚动条滚动,但滚动条可以显示
    H5项目常见问题及注意事项
    利用cookie实现“只弹出一次窗口”的JS代码
    关于JSON.parse在ie6,ie7下未定义的issue
    你真的会使用XMLHttpRequest吗?
    页面内容不足以铺满屏幕高度时,footer居底显示
    CSS box-shadow 属性
  • 原文地址:https://www.cnblogs.com/hdllhd/p/11771218.html
Copyright © 2020-2023  润新知