欢迎光临散文网 会员登陆 & 注册

java链表LinkedList类

2022-07-24 08:55 作者:虚云幻仙  | 我要投稿

/**
* List接口的实现类LinkedList  linked list链表
* ArrayList和Vector都是使用数组来存储数据 数组是空间连续的 在内存中指定长度创建连续的空间 数组的元素与元素之间在空间上是相邻的
* 数组使用index索引所以查询效率高O(1) 但每次增删都涉及到index后面元素的位移 增删效率低
* LinkedList使用双向链表存储数据 每个结点/元素包含数据域(即元素本身的数据)和指针域(指向直接前驱结点和指向直接后继结点的两个指针)
* 链表不记录index 每次查询需要从表头结点或表尾结点沿指针域逐个结点查找 查询效率低 但每次增删只需要更改目标位置前后结点的指针域 不需要对元素进行移位 增删效率高
* LinkedList线程不安全
*/

public class TestLinkedList {
   public static void main(String[] args) {
       List<String> linked = new LinkedList<>();
       //LinkedList实现了List的抽象方法 对于List的方法的调用通过List引用变量来使用        //linked.add();linked.remove();linked.clear();linked.size();linked.toArray();linked.indexOf();linked.set();linked.get();linked.contains();linked.isEmpty();    LinkedList实现的这些基本方法的用法同ArrayList和Vector
       LinkedList<String> ll1 = new LinkedList<>();
       //测试LinkedList类内定义的用于链表操作的方法 这些方法不是实现自List 不能用List引用变量调用
       ll1.addFirst("a");
       //.addFirst()向链表的表头添加结点 每次添加的结点都变更为表头结点
       ll1.addFirst("b");
       ll1.addFirst("c");
       System.out.println(ll1);
       //结果为[c, b, a] 每次addFirst的结点都变更为表头
       ll1.addLast("d");
       //.addLast()向表尾添加结点 每次添加都变更为新的表尾
       ll1.addLast("f");
       for (String e:ll1){
           System.out.print(e+",");
           //结果c,b,a,d,f,
       }
       System.out.println();
       System.out.println(ll1.getFirst());
       //.getFirst()返回表头结点 结果为c
       System.out.println(ll1.getLast());
       //.getLast()返回表尾结点 结果为f
       System.out.println(ll1.removeFirst());
       System.out.println(ll1);
       //.removeFirst()删除表头并返回 删除了c并返回 链表的结果[b, a, d, f]
       System.out.println(ll1.removeLast());
       for (int i = 0 ;i<ll1.size();i++){
           System.out.print(ll1.get(i)+",");
       }
       //.removeLast()删除表尾并返回 删除了f并返回 链表的结果[b, a, d]
       System.out.println();
       System.out.println(ll1.pop());
       //作用同Stack栈容器的pop() 弹出栈顶/表头结点 并返回 相当于removeFirst
       System.out.println(ll1);
       //结果[a, d]
       ll1.push("g");
       //作用同Stack栈容器的push() 压栈 将结点推入栈顶/表头 相当于addFirst
       System.out.println(ll1);
       //结果[g, a, d]

       /* LinkedList通过双向链表存储数据 源码
       public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable
       //泛型类
           private static class Node<E> {
           //node结点 static class 静态内部类 在LinkedList类内部定义
               E item;
               Node<E> next;
               Node<E> prev;
               //item用来保存元素 即数据域  next/previous指针域指向直接后继结点和直接前驱结点
               Node(Node<E> prev, E element, Node<E> next) {
                   this.item = element;
                   this.next = next;
                   this.prev = prev;
               }
               //没有无参构造器 每个结点在实例时必须定义唯三的属性 表头结点的prev=null 表尾结点的next=null
           }
           //回到LinkedList类内
           transient int size = 0;
           //size即结点的总数 作用同ArrayList/Vector.size
           transient Node<E> first;
           transient Node<E> last;
           //LinkedList对象中只保存表头结点first和表尾结点last

           public boolean add(E e) {
               linkLast(e);
               return true;
           }
           //实现List的add()方法 通过向表尾添加结点的方式
           void linkLast(E e) {
               final Node<E> l = last;
               //先将当前表尾缓存 如果链表为空则last=null
               final Node<E> newNode = new Node<>(l, e, null);
               //l赋值给prev e赋值给item next设为null
               last = newNode;
               if (l == null)
                   first = newNode;
               else
                   l.next = newNode;
                   //新结点变为表尾结点,如果链表为空则将first和last都设为新结点 否则将缓存的原表尾结点.next链到新结点
               size++;
               modCount++;
           }
           //每个结点保存前后的指针 LinkedList容器保存头尾的指针 这样做不会在链表容器内保存每一个结点的指针

           public void addFirst(E e) {linkFirst(e);}
           //linkFirst和linkLast原理相同 一个在表头做调整 一个在表尾做调整 操作基本是镜像的 这里addFirst没有返回值 普通add会返回boolean

           public void add(int index, E element) {
               checkPositionIndex(index);
               //判定索引越界
               if (index == size)
                   linkLast(element);
               else
                   linkBefore(element, node(index));
           }
           //判断index==size即向表尾添加结点所以直接调用linkLast() 也同时解决了index=size=0空链表的情况 所以else为链表中至少有一个结点的情况  node(index)方法如下
           Node<E> node(int index) {
               if (index < (size >> 1)) {
               //二分 size即length /2为中位 size为偶数时/2是右半边的首位 小于中位则从头查找更快 大于则从尾查找
                   Node<E> x = first;
                   for (int i = 0; i < index; i++)
                       x = x.next;
                   return x;
                   //将表头赋值给x 每次.next都会返回直接后继结点 从first一直.next.next.next可以通到last 当i=index-1时x.next即为index位结点
               } else {
                   Node<E> x = last;
                   for (int i = size - 1; i > index; i--)
                       x = x.prev;
                   return x;
               }
           }
           //node(index)是查找索引位结点的方法  linkBefore(element, node(index))方法如下
           void linkBefore(E e, Node<E> succ) {
           //传入的succ即为index位结点
               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++;
           }
           //通过index位结点succ找到直接前驱结点pred  new新结点.prev设为前驱结点 .next设为succ 将succ的prev和前驱结点的next改为新结点
           //pred==null即add(0,element)的情况


           public E get(int index) {
               checkElementIndex(index);
               return node(index).item;
           }
           //node()方法返回Node结点 .item即数据域/存放的元素  get()方法返回的是结点的数据,类型<E>,返回的不是结点Node<E>

           public E remove(int index) {
               checkElementIndex(index);
               return unlink(node(index));
           }
           //移除index位结点 先node()返回index位的结点再解除链接unlink()方法如下
           E unlink(Node<E> x) {
           //泛型方法 根据传入的结点类型做调整
               final E element = x.item;
               //缓存要删除结点x的元素作为之后的返回值
               final Node<E> next = x.next;
               final Node<E> prev = x.prev;
               if (prev == null) {
               //prev==null即remove(0) 将first=x改为next
                   first = next;
               } else {
                   prev.next = next;
                   x.prev = null;
               }
               //将前驱结点跳过x链到后继结点
               if (next == null) {
                   last = prev;
               } else {
                   next.prev = prev;
                   x.next = null;
               }
               x.item = null;
               //将指向x的链接全部撤掉 将x的指针全赋null 将x的item赋null x对象和数据对象不再被引用 等待垃圾回收
               size--;
               modCount++;
               return element;
           }

           public boolean remove(Object o) {
               if (o == null) {
               //元素/数据虽然是空 但.item保存空元素的结点是存在的 结点的prev/next可能有值 即空数据结点占了一位
                   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) {
                   //将迭代通过.next.next实现 表尾结点的next为null 所以直到遇到null即遍历完成 这里x=null是判断结点不是上面的x.item=null判断元素 结点只有空链表或表头.prev或表尾.next才会为空
                       if (o.equals(x.item)) {
                       //这里调用了o.equals()方法,如果没有单独判断if(o==null)的话,当o为null时调用方法会报空指针异常
                           unlink(x);
                           return true;
                       }
                   }
               }
               return false;
               //不存在o返回false
       }
        */
   }
}

java链表LinkedList类的评论 (共 条)

分享到微博请遵守国家法律