Java集合:关于LinkedList类

一、LinkedList概念

LinkedList实现类:是一个List集合, 底层数据结构是链表,在内存中可能并不是一个连续的地址,而是在每个节点里存放下一个节点的地址。

LinkedList是基于双向链表实现的,除了可以当作链表操作外,它还可以当作栈、队列和双端队列来使用。

LinkedList同样是非线程安全的,只在单线程下适合使用。

  • LinkedList 继承了 AbstractSequentialList 类。
  • LinkedList 实现了 Queue 接口,可作为队列使用。
  • LinkedList 实现了 List 接口,可进行列表的相关操作。
  • LinkedList 实现了 Deque 接口,可作为队列使用。
  • LinkedList 实现了 Cloneable 接口,可实现克隆。
  • LinkedList 实现了 java.io.Serializable 接口,即可支持序列化,能通过序列化去传输。

特点:

  1. 增删元素效率快。

缺点:

  1. 遍历数据很慢,每次获取数据都需要从头开始。

链表分为单向链表、双向链表、循环链表。

单向链表:

在这里插入图片描述

双向链表:

在这里插入图片描述

循环表链: 头节点和尾节点被连接在一起的链表称为循环链表,这种方式在单向和双向链表中皆可实现。循环链表中第一个节点之前就是最后一个节点,反之亦然。
在这里插入图片描述

LinkedList 类位于 java.util 包中,使用前需要引入它,语法格式如下:

// 引入 LinkedList 类
import java.util.LinkedList; 

LinkedList<E> list = new LinkedList<E>();   // 普通创建方法
或者
LinkedList<E> list = new LinkedList(Collection<? extends E> c); // 使用集合创建链表

二、LinkedList常用方法

2.1作为List集合

方法描述
public boolean add(E e)链表末尾添加元素,返回是否成功,成功为 true,失败为 false。
public void add(int index, E element)向指定位置插入元素。
public boolean addAll(Collection c)将一个集合的所有元素添加到链表后面,返回是否成功,成功为 true,失败为 false。
public boolean addAll(int index, Collection c)将一个集合的所有元素添加到链表的指定位置后面,返回是否成功,成功为 true,失败为 false。
public void addFirst(E e)元素添加到头部。
public void addLast(E e)元素添加到尾部。
public void clear()清空链表。
public E removeFirst()删除并返回第一个元素。
public E removeLast()删除并返回最后一个元素。
public boolean remove(Object o)删除某一元素,返回是否成功,成功为 true,失败为 false。
public E remove(int index)删除指定位置的元素。
public E remove()删除并返回第一个元素。
public boolean contains(Object o)判断是否含有某一元素。
public E get(int index)返回指定位置的元素。
public E getFirst()返回第一个元素。
public E getLast()返回最后一个元素。
public int indexOf(Object o)查找指定元素从前往后第一次出现的索引。
public int lastIndexOf(Object o)查找指定元素最后一次出现的索引。
public E element()返回第一个元素。
public Object clone()克隆该列表。
public int size()返回链表元素个数。
public Iterator descendingIterator()返回倒序迭代器。
public ListIterator listIterator(int index)返回从指定位置开始到末尾的迭代器。
public Object[] toArray()返回一个由链表元素组成的数组。

2.2作为队列

方法描述
public boolean offer(E e)向链表末尾添加元素,返回是否成功,成功为 true,失败为 false。
public boolean offerFirst(E e)头部插入元素,返回是否成功,成功为 true,失败为 false。
public boolean offerLast(E e)尾部插入元素,返回是否成功,成功为 true,失败为 false。
public E peek()返回第一个元素。
public E peekFirst()返回头部元素。
public E peekLast()返回尾部元素。
public E poll()删除并返回第一个元素。
public E pollLast()E pollLast():

2.3作为栈

方法描述
push(E e)将一个元素压入栈顶,也就是向列表头部插入一个元素;
E pop():弹出栈顶元素,即移除列表第一个元素。

2.4代码演示

public class DemoTest {

    public static void main(String[] args) {
        LinkedList<String> list = new LinkedList<String>();
        //添加元素到链表尾部
        list.add("xiaoming");
        //添加元素到链表头部
        list.addFirst("xiaoliang");
        //加元素到链表尾部
        list.addLast("xiaodu");
        System.out.println(list);//[xiaoliang, xiaoming, xiaodu]
        //移除元素头部元素
        list.remove();
        System.out.println(list);//[xiaoming, xiaodu]
        //遍历
        for (int size = list.size(), i = 0; i < size; i++) {
            System.out.println(list.get(i));
            /*结果:
            xiaoming
		   xiaodu		
            */
        }
    }
}

三、LinkedList源码分析

/**
 *用于记录链表的大小
 */
transient int size = 0;
/**
 * 链表第一个节点的引用
 */
transient Node<E> first;
/**
 * 链表最后一个节点的引用
 */
transient Node<E> last;
/**
 * 私有的静态内部类,用于描述双向链表的节点
 */
private static class Node<E> {
    E item; // 节点的值,也就是List中的元素
    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 void add(int index, E element) {
     // 检查index是否合法,index >= 0 && index <= size
     checkPositionIndex(index);
     if (index == size)
         linkLast(element); // 追加到链表尾部
     else
         linkBefore(element, node(index)); 
 }
 /**
 * 将元素添加到链表尾部。
 */
void linkLast(E e) {
    final Node<E> l = last; // 记录链表的最后一个节点
    // 创建新节点,设置其前驱节点为l,值为e,后继节点为null
    final Node<E> newNode = new Node<>(l, e, null); 
    last = newNode; // last指向新创建的这个节点
    /*如果l指向的是null(注意,此时l和last指向的已经不是同一个对象了),
      说明newNode既是第一个节点,又是最后一个节点,也就是说newNode是链表中唯一的一个节点*/
    if (l == null) 
        first = newNode;
    else
        l.next = newNode; // l的后继节点设置为newNode
    size++;
    modCount++;
}
 /**
  * 返回指定索引处的节点
  */
 Node<E> node(int index) {
     // assert isElementIndex(index);
	 // 可以看出这里是通过遍历的方式查找index处的节点。
	 // 为了减少遍历的节点个数,提高查找效率,这里判断了index是在链表的前半段还是后半段,
	 // 从而实现从半个链表中查找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;
     }
 }
 /**
  * 将元素包装成链表节点插入到指定节点的前面
  */
 void linkBefore(E e, Node<E> succ) {
     // assert succ != null;
     final Node<E> pred = succ.prev; // 记录succ的前驱节点
     final Node<E> newNode = new Node<>(pred, e, succ); // 创建新节点newNode,值为e
     succ.prev = newNode; // 将succ的前驱节点设置为newNode
     if (pred == null) // 此时链表的第一个节点是newNode
         first = newNode;
     else
         pred.next = newNode;
     size++;
     modCount++;
 }
 /**
  * 获取指定索引处的元素
  */
 public E get(int index) {
     // 索引合法性检查。index >= 0 && index < size
     checkElementIndex(index);
     return node(index).item; // 通过遍历查找节点并获取元素
 }
 /**
  * 替换列表中指定位置的元素,返回被替换的元素。
  * 核心代码依旧是通过遍历查找节点
  */
 public E set(int index, E element) {
     checkElementIndex(index);
     Node<E> x = node(index);
     E oldVal = x.item;
     x.item = element;
     return oldVal;
 }
 /**
  * 移除列表中指定位置的元素
  */
 public E remove(int index) {
     checkElementIndex(index);
     // 先遍历查找index处的节点,再调用unlink方法
     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) { // 被删除节点的前驱节点为null,说明被删除节点是链表的第一个节点
         first = next; // 此时链表的第一个节点是被删除节点的后继节点
     } else {
         prev.next = next; 
         x.prev = null; 
     }

     if (next == null) { // 被删除节点的后继节点为null,说明被删除节点是链表的最后一个节点
         last = prev; // 此时链表的最后一个节点是被删除节点的前驱节点
     } else {
         next.prev = prev;
         x.next = null;
     }

     x.item = null; // 彻底释放被删除节点
     size--; // 表长减一
     modCount++;
     return element;
 }

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值