集合-LinkedList

LinkedList

LinkedList的概述

LinkedList的底层使用双向链表实现。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YFvfuWIM-1681288911283)(E:\Java笔记\javaSE\集合(容器)(9)\集合\LinkedList.assets\image-20230406115511053.png)]

链表是一种线性数据结构,其中每个元素都是一个单独的对象,包含一个指向列表中下一个节点的引用。

它可以用于实现各种抽象数据类型,例如列表、堆栈、队列等。

LinkedList的优缺点

优点

  • 插入和删除操作的时间复杂度为O(1),相比于数组的O(n)更加高效。
  • 链表的大小可以动态调整,不像数组需要预先分配空间。
  • 链表的节点可以在内存中部连续存储,因此可以更加的灵活的利用内存空间。

缺点

  • 访问元素的时间复杂度为O(n),相比于数组的O(1)较低效。
  • 链表需要额外的空间来存储指向下一个节点的指针,因此占用的空间比数组更大。
  • 链表的随机访问效率比较低,因为需要从头开始遍历链表才能找到指定位置的节点。

LinkedList的创建方式

LinkedList linkedList = new LinkedList();
//向上转型
List linkedLists = new LinkedList();

常用方法,以及源码分析

方法返回类型解释
add(E e)boolean将指定元素添加到此列表的结尾
add(int index, E element)void将此列表中指定的位置插入指定的元素
addFirst(E e)void将指定元素插入此列表的开头
addLast(E e)void将指定元素添加到此列表的结尾
clear()void从此列表中移除所有元素
get(int index)E返回此列表中指定位置处的元素
getFirst()E返回此列表的第一个元素
remove()E获取并移除此列表的头(第一个元素)
remove(int index)E移除此列表中指定位置的元素
removeFirst()E移除并返回此列表的第一个元素
removelast()E移除并返回此列表的最后一个元素
set(int index,E element)E将此列表中指定位置的元素替换为指定的元素
size()int返回此列表的元素数

add(E e)

源码分析:

通过调用 linkLast(E e) 方法将元素添加到链表的末尾。

linkLast(E e) 方法创建一个新的节点,并将其插入到链表的末尾。如果链表为空,则将新节点设置为第一个节点。否则,将新节点链接到最后一个节点。最后,更新链表的大小和 modCount 变量。

	public boolean add(E e) {
        //调用linklast方法将元素添加到链表的尾端
    	linkLast(e);
        //添加成功,返回true
    	return true;
	}
	void linkLast(E e) {
        //获取链表的最后一个节点
        final Node<E> l = last;
        //创建最后一个节点,他的前驱节点是l,后去节点为null,数据为e
        final Node<E> newNode = new Node<>(l, e, null);
        //将链表的尾节点指向新节点
        last = newNode;
        //判断节点是否为null
        if (l == null)
            //为null则将节点指向新节点
            first = newNode;
        else
            //如果不为null,将原尾节点的后继节点指向新节点
            l.next = newNode;
        //聊表的大小加1
        size++;
        //修改计数器
        modCount++;
    }

案例:
public static void main(String[] args) {
        LinkedList linkedList = new LinkedList();
    	//添加数据
        linkedList.add("张三");
        linkedList.add("李四");
    	//输出此列表
        System.out.println(linkedList);
    }

add(int index, E element)

源码分析:

将元素添加到指定位置

public void add(int index, E element) {
   		 //检查索引是否越界
        checkPositionIndex(index);
		//索引与长度相等
        if (index == size)
            //在链表尾部添加数据
            linkLast(element);
        else
            //在指定位置之前添数据
            linkBefore(element, node(index));
    }

    private void checkPositionIndex(int index) {
        //判断索引是否合法
        if (!isPositionIndex(index))
            //不合法则抛出下标越界异常
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    void linkLast(E e) {
        //获取链表的最后一个节点
        final Node<E> l = last;
        //创建一个新的节点,他的前驱节点是l,后去节点为null,数据为e
        final Node<E> newNode = new Node<>(l, e, null);
        //将链表的最后一个节点指向新节点
        last = newNode;
        //判断链表是否为null
        if (l == null)
            //将第一个节点指向新节点
            first = newNode;
        //如果不为null
        else
            //将最后一个节点的下一个节点指向新节点
            l.next = newNode;
        //链表大小加1
        size++;
        //修改计数器加1
        modCount++;
    }
    void linkBefore(E e, Node<E> succ) {
        //获取指定位置的前一个节点
        final Node<E> pred = succ.prev;
        //创建一个新节点
        final Node<E> newNode = new Node<>(pred, e, succ);
        //将指定位置的前一个节点的下一个节点指向新节点
        succ.prev = newNode;
        //如果指定位置的前一个节点为null,
        if (pred == null)
            //将第一个节点指向新节点
            first = newNode;
        //如果指定位置的前一个节点不为null
        else
            //将前一个节点的下一个节点指向新节点
            pred.next = newNode;
        //链表大小加1
        size++;
        //修改计数器加1
        modCount++;
    }
案例:
public static void main(String[] args) {
    LinkedList linkedList = new LinkedList();
    linkedList.add("张三");
    linkedList.add("李四");
    System.out.println(linkedList);
    //位置1,添加数据
    linkedList.add(1,"王五");
    System.out.println(linkedList);
}

addFirst(E e)

源码分析:

将指定元素插入到此列表的开头

public void addFirst(E e) {
    // 在链表的头部添加一个新节点
    linkFirst(e);
}
private void linkFirst(E e) {
    // 获取头节点
	final Node> f = first;
	// 创建一个新节点,它的前驱节点为null,数据为e,后继节点为头节点f
	final Node> newNode = new Node<>(null, e, f);
	// 将头节点指向新节点
	first = newNode;
	// 如果头节点为null,说明
	if (f == null)
        //链表为空,将尾节点指向新节点
		last = newNode;
	// 否则将头节点的前驱节点指向新节点
	else
		f.prev = newNode;
	// 链表长度加1
	size++;
	// 修改次数加1
	modCount++;
  }
案例:
public static void main(String[] args) {
    LinkedList linkedList = new LinkedList();
    linkedList.add("张三");
    linkedList.add("李四");
    System.out.println(linkedList);

    linkedList.add(1,"王五");
    System.out.println(linkedList);
	//列表开头添加数据
    linkedList.addFirst("赵六");
    System.out.println(linkedList);

}

addLast(E e)

源码分析:

将指定元素添加到此列表的结尾

 public void addLast(E e) {
     //在链表的尾部添加一个新节点
        linkLast(e);
    }
void linkLast(E e) {
        //获取链表的最后一个节点
        final Node<E> l = last;
        //创建一个新的节点,他的前驱节点是l,后去节点为null,数据为e
        final Node<E> newNode = new Node<>(l, e, null);
        //将链表的最后一个节点指向新节点
        last = newNode;
        //判断链表是否为null
        if (l == null)
            //将第一个节点指向新节点
            first = newNode;
        //如果不为null
        else
            //将最后一个节点的下一个节点指向新节点
            l.next = newNode;
        //链表大小加1
        size++;
        //修改计数器加1
        modCount++;
    }
案例:
public static void main(String[] args) {
    LinkedList linkedList = new LinkedList();
    linkedList.add("张三");
    linkedList.add("李四");
    System.out.println(linkedList);

    linkedList.add(1,"王五");
    System.out.println(linkedList);

    linkedList.addFirst("赵六");
    System.out.println(linkedList);
	//列表结尾添加数据
    linkedList.addLast("唐七");
    System.out.println(linkedList);
}

clear():

源码分析:

从此列表中移除所有元素

public void clear() {
    // 遍历链表中的每一个节点
    for (Node<E> x = first; x != null; ) {
        // 保存当前节点的下一个节点
        Node<E> next = x.next;
        // 将当前节点的数据项置为null
        x.item = null;
        // 将当前节点的前驱和后继节点都置为null
        x.next = null;
        x.prev = null;
        // 将当前节点指向下一个节点
        x = next;
    }
    // 将链表的头节点和尾节点都置为null
    first = last = null;
    // 将链表的大小置为0
    size = 0;
    // 修改计数器
    modCount++;
}
案例:
public static void main(String[] args) {
    LinkedList linkedList = new LinkedList();
    
    linkedList.add("张三");
    linkedList.add("李四");
    linkedList.add(1,"王五");
    linkedList.addFirst("赵六");
    linkedList.addLast("唐七");
    System.out.println(linkedList);

    //清除此列表数据
    linkedList.clear();
    System.out.println(linkedList);;
}

get(int index)

源码分析:

返回此列表中指定位置处的元素

public E get(int index) {
    //校验索引是否合法
    checkElementIndex(index);
    //返回该索引对应的节点的数据
    return node(index).item;
}
private void checkPositionIndex(int index) {
        //判断索引是否合法
        if (!isPositionIndex(index))
            //不合法则抛出下标越界异常
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
Node<E> node(int 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;
        }
    }
案例:
public static void main(String[] args) {
    LinkedList linkedList = new LinkedList();
    linkedList.add("张三");
    linkedList.add("李四");
    linkedList.add(1,"王五");
    linkedList.addFirst("赵六");
    linkedList.addLast("唐七");
    System.out.println(linkedList);

    //获取指定位置的数据
    System.out.println(linkedList.get(2));
}

getFirst()

源码分析:

返回此列表的第一个元素

public E getFirst() {
    //获取链表的头节点
    final Node<E> f = first;
    //如果头节点为null
    if (f == null)
        //抛出NoSuchElementException异常
        throw new NoSuchElementException();
    //返回头节点的数据
    return f.item;
}
案例:
public static void main(String[] args) {
    LinkedList linkedList = new LinkedList();
    linkedList.add("张三");
    linkedList.add("李四");
    linkedList.add(1,"王五");
    linkedList.addFirst("赵六");      
    linkedList.addLast("唐七");
    System.out.println(linkedList);
    //获取此列表的第一个节点
    System.out.println(linkedList.getFirst());

}

remove()

源码分析:

获取并移除此里列表的头(第一个元素)

public E remove() {
    //调用移除第一个元素方法
    return removeFirst();
}
public E removeFirst() {
    // 获取链表的头节点
    final Node<E> f = first;
    // 如果头节点为空,
    if (f == null)
        //抛出NoSuchElementException异常
        throw new NoSuchElementException();
    // 返回调用调用unlinkFirst方法删除头节点并返回删除的元素
    return unlinkFirst(f);
}
 private E unlinkFirst(Node<E> f) {
    // assert f == first && f != null;
     // 获取头节点的元素
    final E element = f.item;
     // 获取头节点的下一个节点
    final Node<E> next = f.next;
     // 将头节点的元素置为空
    f.item = null;
     // 将头节点的下一个节点置为空,以便垃圾回收
    f.next = null; // help GC
     // 将链表的头节点指向原头节点的下一个节点
    first = next;
     // 如果原头节点的下一个节点为空
    if (next == null)
        //将链表的尾节点也置为空
        last = null;
    else
        // 否则将原头节点的下一个节点的前一个节点置为空
        next.prev = null;
     // 链表的大小减1
    size--;
     // 修改计数器加1
    modCount++;
     // 返回删除的元素
    return element; 
}
案例:
public static void main(String[] args) {
    LinkedList linkedList = new LinkedList();
    linkedList.add("张三");
    linkedList.add("李四");
    linkedList.add(1,"王五");
    linkedList.addFirst("赵六");
    linkedList.addLast("唐七");
    
    //获取并移除此列表的第一个节点
    System.out.println(linkedList.remove());
    //输出此列表的全部数据
    System.out.println(linkedList);
}

remove(int index)

源码分析:

移除指定列表中的指定位置的元素

//删除指定位置的节点
public E remove(int index) {
    // 检查索引是否越界
    checkElementIndex(index);
    // 删除并返回指定位置的节点
    return unlink(node(index));
}
// 检查索引是否越界
private void checkElementIndex(int index) {
    // 如果索引越界
    if (!isElementIndex(index))
        //则抛出IndexOutOfBoundsException异常
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
// 删除指定节点
E unlink(Node<E> x) {
    // 获取节点的值	
    final E element = x.item;
    // 获取下一个节点
    final Node<E> next = x.next;
    // 获取上一个节点
    final Node<E> prev = x.prev;

    // 如果上一个节点为空
    if (prev == null) {
        // 将下一个节点设为头节点
        first = next;
    } else {
	    // 将上一个节点的下一个节点设为当前节点的下一个节点
        prev.next = next;
        // 将当前节点的上一个节点设为空
        x.prev = null;
    }

    // 如果下一个节点为空
    if (next == null) {
        // 将上一个节点设为尾节点
        last = prev;
    } else {
        // 将下一个节点的上一个节点设为当前节点的上一个节点
        next.prev = prev;
        // 将当前节点的下一个节点设为空
        x.next = null;
    }
	
    // 将当前节点的值设为空
    x.item = null;
    // 链表大小减一
    size--;
    // 修改次数加一
    modCount++;
    // 返回删除的节点的值
    return element;
}

// 获取指定位置的节点
Node<E> node(int 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;
    }
}
案例:
public static void main(String[] args) {
    LinkedList linkedList = new LinkedList();
    linkedList.add("张三");
    linkedList.add("李四");
    linkedList.add(1,"王五");
    linkedList.addFirst("赵六");
    linkedList.addLast("唐七");

    //移除此列表的指定位置的节点
    System.out.println(linkedList.remove(1));
    //输出此列表的全部数据
    System.out.println(linkedList);
}

removeFirst()

源码分析:

移除并返回列表的第一个元素

// 删除链表的头节点
public E removeFirst() {
    // 获取头节点
    final Node<E> f = first;
    // 如果头节点为空,
    if (f == null)
        //抛出NoSuchElementException异常
        throw new NoSuchElementException();
    // 否则,调用unlinkFirst方法删除头节点
    return unlinkFirst(f);
}
 private E unlinkFirst(Node<E> f) {
    // assert f == first && f != null;
     // 获取头节点的元素
    final E element = f.item;
     // 获取头节点的下一个节点
    final Node<E> next = f.next;
     // 将头节点的元素置为空
    f.item = null;
     // 将头节点的下一个节点置为空,以便垃圾回收
    f.next = null; // help GC
     // 将链表的头节点指向原头节点的下一个节点
    first = next;
     // 如果原头节点的下一个节点为空
    if (next == null)
        //将链表的尾节点也置为空
        last = null;
    else
        // 否则将原头节点的下一个节点的前一个节点置为空
        next.prev = null;
     // 链表的大小减1
    size--;
     // 修改计数器加1
    modCount++;
     // 返回删除的元素
    return element; 
}
案例:
public static void main(String[] args) {
    LinkedList linkedList = new LinkedList();
    linkedList.add("张三");
    linkedList.add("李四");
    linkedList.add(1,"王五");
    linkedList.addFirst("赵六");
    linkedList.addLast("唐七");

    //移除并返回列表的第一个元素
    System.out.println(linkedList.removeFirst());
    //输出此列表的全部数据
    System.out.println(linkedList);
}

removelast()

源码分析:

移除并返回此列表最后一个元素

// 从链表的尾部删除一个节点
public E removeLast() {
    // 获取尾节点
    final Node<E> l = last;
    // 如果尾节点为空
    if (l == null)
        //抛出NoSuchElementException异常
        throw new NoSuchElementException();
    // 调用unlinkLast方法删除尾节点
    return unlinkLast(l);
}
// 删除链表中的一个节点
private E unlinkLast(Node<E> l) {
    // assert l == last && l != null;
    // 获取节点的数据
    final E element = l.item;
    // 获取节点的前一个节点
    final Node<E> prev = l.prev;
    // 将节点的数据置为空
    l.item = null;
    // 将节点的前一个节点置为空	
    l.prev = null; // help GC
    // 将尾节点指向前一个节点
    last = prev;
    // 如果前一个节点为空
    if (prev == null)
        // 将头节点置为空
        first = null;
    else
        // 将前一个节点的指针置为空
        prev.next = null;
    // 链表大小减1
    size--;
    // 修改计数器加1
    modCount++;
    // 返回被删除节点的数据
    return element;
}
案例:
public static void main(String[] args) {
    LinkedList linkedList = new LinkedList();
    linkedList.add("张三");
    linkedList.add("李四");
    linkedList.add(1,"王五");
    linkedList.addFirst("赵六");
    linkedList.addLast("唐七");

    //移除并返回列表的最后一个元素
    System.out.println(linkedList.removeLast());
    //输出此列表的全部数据
    System.out.println(linkedList);
}

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;
}
 private void checkElementIndex(int index) {
     // 如果索引越界
    if (!isElementIndex(index))
        //抛出IndexOutOfBoundsException异常
        throw new IndexOutOfBoundsException(outOfBoundsMsg(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;
        }
    }
案例:
public static void main(String[] args) {
    LinkedList linkedList = new LinkedList();
    linkedList.add("张三");
    linkedList.add("李四");
    linkedList.addFirst("赵六");
    linkedList.addLast("唐七");
    System.out.println("原数据:"+linkedList);

    //指定位置的值替换为指定的元素
    linkedList.set(2,"小明");
    System.out.println("修改后的数据:"+linkedList);
}

size()

源码分析:

返回此列表的元素个数

public int size() {
    //返回链表的长度
    return size;
}
案例:
public static void main(String[] args) {
    LinkedList linkedList = new LinkedList();
    linkedList.add("张三");
    linkedList.add("李四");
    linkedList.addFirst("赵六");
    linkedList.addLast("唐七");

    System.out.println(linkedList.size());
}

线程安全

LinkedList不是线程安全的,如果需要在多线程环境下使用LinkedList,需要使用Collections.synchronizedList方法将其包装成线程安全的List

List<String> linkedList = new LinkedList<>();
List<String> synchronizedList = Collections.synchronizedList(linkedList); 

观众老爷看完觉得不错点个赞
在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陆卿之

你的鼓励将是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值