LinkedList底层刨析

一、概述

● 底层原理:双向链表(动态数据结构)
● 特点:
①有序可重复(包含null 值),可通过索引检索。
②具有高效的插入和删除操作,但在随机访问时效率较低。
③可以用作栈(stack)和队列(queue)这两种数据结构的底层实现
④非线程安全,如果需要在多线程环境中使用链表,并保证线程安全性,可以采取以下两种方式,使用同步机制:可以使用 synchronized 关键字或其他线程安全的同步机制来保护对 LinkedList 的并发访问,例如:List<String> synchronizedList = Collections.synchronizedList(new LinkedList<>());或者使用线程安全的替代类:例如 CopyOnWriteArrayList、ConcurrentLinkedDeque 等。

二、双向链表数据结构

在这里插入图片描述

三、向LinkedList对象中添加元素

boolean add(E e):将指定元素添加到列表末端,等价于addLast(E e)

    public boolean add(E e) {
    	linkLast(e);	//将元素添加到列表尾部
    	return true;
    }

    void linkLast(E e) {
        final Node<E> l = last;	//获取当前列表的最后一个节点元素
        final Node<E> newNode = new Node<>(l, e, null);	//创建一个新的节点 newNode,将元素 e 存储在该节点中,并将其前驱节点指向 l,后继节点指向 null
        last = newNode;		// 重新赋值
        if (l == null)
            first = newNode;	//链表之前为空,将新节点设置为头节点
        else
            l.next = newNode;	//之前的最后一个节点 l 不为空,则将其后继节点指向新节点
        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;
        }
    }

在这里插入图片描述

void add(int index, E element):指定索引位置添加元素

public void add(int index, E element) {
    checkPositionIndex(index);	//校验索引是否越界,索引小于 0 或者大于链表的大小

    if (index == size)
        linkLast(element);		//将元素添加到列表尾部
    else
        linkBefore(element, node(index));	//在列表中间添加元素
}

    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);  //创建新节点,新节点的pre就是succ的pre节点,新节点的next就是succ节点
        succ.prev = newNode;  //succ.pre指向新节点
        if (pred == null)
            first = newNode;		//新节点为头节点
        else
            pred.next = newNode;	//succ的上一节点的next指向新节点
        size++;
        modCount++;
    }

在这里插入图片描述

四、向LinkedList对象中删除元素

boolean remove(Object 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 remove(int index):移除该列表中指定位置的元素

public E remove(int index) {
    checkElementIndex(index);	//校验索引是否越界
    return unlink(node(index));		//调用该方法将该节点从链表中删除
}

private void checkElementIndex(int index) {
    if (!isElementIndex(index))
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

private String outOfBoundsMsg(int index) {
    return "Index: "+index+", Size: "+size;
}
E unlink(Node<E> x) {
    // assert x != null;
    final E element = x.item;     //将节点 x 的元素保存到变量 element 中,用于最后返回被删除的元素
    final Node<E> next = x.next;   //获取节点 x 的后一个节点 next
    final Node<E> prev = x.prev;   //获取节点 x 的前一个节点 prev

    if (prev == null) {
        first = next;  //节点 x 是链表的头节点,则将链表的 first 指针指向 next,表示删除了头节点。
    } else {
        prev.next = next;  // prev 的 next 指针指向 next,跳过节点 x,使得链表仍然连续
        x.prev = null;
    }

    if (next == null) {
        last = prev;      //节点 x 是链表的尾节点,则将链表的 last 指针指向 prev,表示删除了尾节点
    } else {
        next.prev = prev;  //将 next 的 prev 指针指向 prev,跳过节点 x,使得链表仍然连续。
        x.next = null;
    }

    x.item = null; //将节点 x 的元素设为 null,以帮助垃圾回收器回收内存
    size--;
    modCount++;
    return element;
}

在这里插入图片描述

五、对LinkedList对象中修改元素

E set(int index, E element):修改此列表中当前索引元素的值

public E set(int index, E element) {
    checkElementIndex(index);		//校验索引是否越界
    Node<E> x = node(index);		
    E oldVal = x.item;
    x.item = element;
    return oldVal;
}

六、获取LinkedList对象中的元素

E get(int index) :获取此列表中当前索引位置元素的值

public E get(int index) {
    checkElementIndex(index);	//校验索引是否越界
    return node(index).item;
}

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;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值