Java集合之LinkedList

这篇具有很好参考价值的文章主要介绍了Java集合之LinkedList。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

基本介绍 

常用方法

源码解析

1. LinkedList的底层结构,Node双向链表

2. LinkedList的几个内部变量

3. getFirst()

4. removeFirst()

5. addFirst(E e)

6. contains(Object o)

7. add(E e)

8. remove(Object o)

9. addAll(int index, Collection c)

10. get(int index)

11. spliterator()

总结


基本介绍 

LinkedList是实现了List和Deque接口的双向链表,实现了所有可选列表的操作,允许存放所有元素(包括null)

java linkedlist,# 集合,java,链表

LinkedList是非线程安全的,因此在多线程的情况下,需要加同步锁来保证线程安全,或者使用Collections.synchronizedList 

常用方法

public LinkedList()
public LinkedList(Collection<? extends E> c)
public E getFirst()  获取第一个元素
public E getLast()  获取最后一个元素
public boolean add(E e)  添加一个元素
public void add(int index, E element)  在指定位置插入元素
public void addFirst(E e)  在集合前部增加一个元素
public void addLast(E e)  在集合尾部增加一个元素
public boolean addAll(Collection<? extends E> c)  把另一个集合全部添加到当前集合
public boolean addAll(int index, Collection<? extends E> c) 在指定位置把另一个集合全部添加到当前集合
public boolean contains(Object o)  判断是否包含
public int size()  返回集合大小
public boolean remove(Object o)  根据指定对象删除元素
public E remove(int index)  根据指定索引删除元素
public E removeFirst()  删除第一个元素
public E removeLast()  删除最后一个元素
public E get(int index)  根据索引获取元素
public E set(int index, E element)  在指定索引处添加一个元素(覆盖原数据)
public int indexOf(Object o)  返回指定元素索引
public int lastIndexOf(Object o)  返回指定元素出现的最后一个索引

源码解析

1. LinkedList的底层结构,Node双向链表

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;
    }
}

2. LinkedList的几个内部变量

transient int size = 0;

// 标记链表第一个元素
transient Node<E> first;

// 标记链表最后一个元素
transient Node<E> last;

// 链表结构修改的次数
protected transient int modCount = 0;

3. getFirst()

public E getFirst() {
    final Node<E> f = first;
    if (f == null)
        throw new NoSuchElementException();
    return f.item;
}

这个方法比较简单,其实就是取链表的first节点,getLast()方法类似,不再赘述

4. removeFirst()

public E removeFirst() {
    // 拿到first节点
    final Node<E> f = first;
    if (f == null)
        throw new NoSuchElementException();
    return unlinkFirst(f);
}

private E unlinkFirst(Node<E> f) {
    final E element = f.item;
    final Node<E> next = f.next;
    // 把需要删除的节点的值置为空,并且把其引用也置为空
    f.item = null;
    f.next = null; // help GC
    // 把next节点置为first节点
    first = next;
    // 如果next节点为空,说明整个链表为空
    if (next == null)
        // 则把last节点也置为空
        last = null;
    else
        // 把next节点的前置引用置为空,因为prev节点已经删除,next节点已经为头节点
        // 此时原来的first节点的引用已经全部置空,下一次GC时会把这个节点回收
        next.prev = null;

    // 容器的size - 1
    size--;
    modCount++;
    return element;
}
removceLast()方法类似,只是反过来将最后一个元素置空。

5. addFirst(E e)

public void addFirst(E e) {
    linkFirst(e);
}

private void linkFirst(E e) {
    final Node<E> f = first;
    // 实例化一个Node节点,设置前置节点为null,原来的first节点置为当前节点的next节点
    final Node<E> newNode = new Node<>(null, e, f);

    // 把链表的first节点置为当前节点
    first = newNode;
    // 如果当前链表为空
    if (f == null)
        // 则将last节点也置为新实例化的节点
        last = newNode;
    else
        // 否则,将原有的first节点的前置节点置为当前节点
        f.prev = newNode;
    size++;
    modCount++;
}

addLast() 也类似,不再详细说明了

6. contains(Object o)

public boolean contains(Object o) {
    return indexOf(o) != -1;
}

public int indexOf(Object o) {
    int index = 0;
    // 判断元素是否为空
    if (o == null) {
        // 循环找到对应元素,找到第一个相同元素则返回当前索引
        for (Node<E> x = first; x != null; x = x.next) {
            if (x.item == null)
                return index;
            index++;
        }
    } else {
        for (Node<E> x = first; x != null; x = x.next) {
            if (o.equals(x.item))
                return index;
            index++;
        }
    }
    return -1;
}

7. add(E e)

public boolean add(E e) {
    // 将元素添加到链表的最后一位
    linkLast(e);
    return true;
}

void linkLast(E e) {
    final Node<E> l = last;
    // 实例化一个Node节点,将原来的last节点置为当前节点的前置节点,后置节点置为null
    final Node<E> newNode = new Node<>(l, e, null);
    // 将last节点置为当前节点
    last = newNode;
    // 如果原先的last节点为空,说明之前链表为空
    if (l == null)
        // 将first节点置为当前节点
        first = newNode;
    else
        // 否则将之前last节点的next节点置为当前节点
        l.next = newNode;
    size++;
    modCount++;
}

8. remove(Object o)

public boolean remove(Object o) {
    // 跟其他方法很类似,先判断元素是否为null
    if (o == null) {
        // 循环找到对应元素,并执行unlink方法
        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 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节点
        first = next;
    } else {
        // 否则就把当前节点的前置节点的next节点置为当前节点的next节点
        prev.next = next;
        // 把待删除节点的前置节点置为null
        x.prev = null;
    }

    // 如果待删除节点的next节点为null,说明当前节点是最后一个节点
    if (next == null) {
        // 将当前链表的last节点置为待删除节点的前置节点
        last = prev;
    } else {
        // 把当前节点的next节点的前置节点置为当前节点的前置节点
        next.prev = prev;
        // 当前节点的next节点置为null
        // 此时待删除节点的引用关系已经全部置空
        x.next = null;
    }
    // 把当前节点的数据置为null
    x.item = null;
    size--;
    modCount++;
    return element;
}

9. addAll(int index, Collection<? extends E> c)

/**
 * 在索引位置插入新的集合
 *
 * @Param index 索引
 * @Param c 需要添加的集合
 */
public boolean addAll(int index, Collection<? extends E> c) {
    // 校验index是否超出范围
    checkPositionIndex(index);

    // 将集合转成数组
    Object[] a = c.toArray();
    int numNew = a.length;

    // 如果需要添加的集合为空,直接return false
    if (numNew == 0)
        return false;

    Node<E> pred, succ;

    // 如果索引位置等于当前集合的size,说明要将新的集合添加在链表的尾部
    if (index == size) {
        succ = null;
        pred = last;
    } else {
        // 把当前索引的元素赋值给succ
        succ = node(index);
        // 当前索引的前置节点赋值给pred
        pred = succ.prev;
    }

    // 循环数组
    for (Object o : a) {
        Node<E> newNode = new Node<>(pred, e, null);
        // 如果pred为null,则将新node对象赋值给first
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        // 把pred置为当前元素,继续添加下一个元素
        pred = newNode;
    }
    
    // 如果是在队尾添加元素
    if (succ == null) {
        // 则将链表的last节点置为pred,因为上面在循环末尾会将pred = newNode,此时pred就是最后一个元素
        last = pred;
    } else {
        pred.next = succ;
        succ.prev = pred;
    }

    size += numNew;
    modCount++;
    return true;
}

private void checkPositionIndex(int index) {
    if (!isPositionIndex(index))
        // 如果超出范围就抛出IndexOutOfBoundsException
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

private boolean isPositionIndex(int index) {
    return index >= 0 && index <= size;
}

10. get(int index)

public E get(int index) {
    // 校验索引是否越界,如果越界则抛异常
    checkElementIndex(index);
    // 返回当前节点的元素
    return node(index).item;
}

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

private boolean isElementIndex(int index) {
    return index >= 0 && index < size;
}

Node<E> node(int index) {
    // 判断索引是靠近前半部分还是后半部分
    if (index < (size >> 1)) {
        // 如果索引靠前,则从头开始遍历,直到当前索引
        Node<E> x = first;
        for (int i = 0; i < index; i++)
            x = x.next;
        // 然后返回当前索引对应的node
        return x;
    } else {
        Node<E> x = last;
        // 如果索引靠后,则从末尾开始向前遍历
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}

11. spliterator()

LinkedList中还有spliterator拆分器,该拆分器是后期绑定的,并且使用与链接列表相同的元素快速失效。后期绑定拆分器绑定到元素源意味着在第一次遍历、第一次拆分或第一次查询估计大小时链接列表,而不是在创建拆分器时。它可以和 Java 8 中的 Streams 一起使用。此外,它还可以单独和批量遍历元素。Spliterator 是遍历元素的更好方法,因为它对元素提供了更多的控制。

这个拆分器感觉在我平时的工作中用的也不多,就不做解析了,如果大家想看看这个方法的解析的话给我留言,我再单独写一篇这个的解析。

总结

1. LinkedList底层是由双向链表实现的

2. LinkedList插入和删除比较快,因为正常插入都是插入链表的最后一个元素,只需要改变前置节点的next指向和当前节点的前置指向即可,不需要想ArrayList一样移动数组

3. LinkedList如果按照索引查询会很慢,因为他需要从头或从尾开始逐个遍历直到对应索引位置,不支持随机访问

4. LinkedList是非线程安全的

5. LinkedList元素允许为null,允许重复元素

6. LinkedList是基于双向链表实现的,所以不存在容量不足的情况,不需要扩容文章来源地址https://www.toymoban.com/news/detail-608155.html

到了这里,关于Java集合之LinkedList的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java集合篇之深入解析LinkedList

    作为ArrayList的同门师兄弟,LinkedList的师门地位逊色不少,除了在做算法题的时候我们会用到它之外,在实际的开发工作中我们极少使用它,就连它的创造者都说:“I wrote it,and I never use it”,想想颇有点好笑,但这并不影响我们去学习它,个人认为它底层的链表逻辑对于我

    2024年02月19日
    浏览(37)
  • java源码----集合系列1----ArrayList,linkedList

    底层是一个object数组 Arraylist 是java里面Collection  标准的一个集合,其 底层是一个object数组 。当new一个空参的ArrayList的时候,会默认生成一个空数组。 Arraylist上限是 Integer.MAX_VALUE - 8(Integer.MAX_VALUE  =  2^31-1) ; 超过上限会报内存溢出 这里为什么是Integer.MAX_VALUE-8  ,源码上的解

    2024年02月03日
    浏览(43)
  • java集合框架(二)LinkedList常见方法的使用

    @[toc] ## 一、什么是LinkedList LinkedList是Java中的一个双向链表。 它实现了List和Deque接口,在使用时可以像List一样使用元素索引,也可以像Deque一样使用队列操作。 LinkedList每个节点都包含了前一个和后一个节点的引用,因此可以很方便地在其中进行节点的插入、删除和移动。 相

    2024年02月05日
    浏览(43)
  • Java基础——LinkedList集合实现栈和队列

    (1)LinkedList的特点: 底层数据结构是双链表,查询慢,首尾操作的速度是极快的,所以多了很多首位操作的特有API。 (2)LinkedList集合的特有功能: 方法名称 说明 public void addFirst(E e) 在该列表开头插入指定的元素 public void addLast(E e) 将指定的元素追加到此列表的末尾 publ

    2023年04月12日
    浏览(44)
  • Java语言----LinkedList 和 链表的实现

    目录 一.链表概念 二.链表的分类  三.无头单向非循环链表的实现 3.1创建简单链表 3.2 链表基本方法实现 3.3四大基本功能              3.3.1.增加元素结点              3.3.2.查找元素结点              3.3.3.删除元素结点              3.3.4.结点信息修改   四.LinkedList是什

    2024年02月02日
    浏览(49)
  • 【JAVA语言-第15话】集合框架(二)——List、ArrayList、LinkedList、Vector集合

    目录 List集合 1.1 概述 1.2 特点 1.3 常用方法 1.4 ArrayList集合 1.4.1 概述  1.4.2 练习 1.5 LinkedList集合  1.5.1 概述 1.5.2 特点 1.5.3 常用方法 1.5.4 练习 1.6 Vector类 1.6.1 概述 1.6.2 练习 1.7 List实现类的异同点         java.util.List: List是一个接口,它继承自Collection接口。 常用的实现

    2024年01月25日
    浏览(57)
  • 数据结构(Java实现)LinkedList与链表(下)

    ** ** 结论 让一个指针从链表起始位置开始遍历链表,同时让一个指针从判环时相遇点的位置开始绕环运行,两个指针都是每次均走一步,最终肯定会在入口点的位置相遇。 LinkedList的模拟实现 单个节点的实现 尾插 运行结果如下: 也可以暴力使用 全部代码 MyLinkedList IndexOut

    2024年02月11日
    浏览(45)
  • 数据结构(Java实现)LinkedList与链表(上)

    链表 逻辑结构 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。 无头双向链表:在Java的集合框架库中LinkedList底层实现就是无头双向循环链表。 链表的实现 创建一个链表 遍历单链表 、 得到

    2024年02月11日
    浏览(51)
  • 【JAVA学习笔记】53 - 集合-List类及其子类Collection、ArrayList、LinkedList类

    https://github.com/yinhai1114/Java_Learning_Code/tree/main/IDEA_Chapter14/src/com/yinhai/collection_ https://github.com/yinhai1114/Java_Learning_Code/tree/main/IDEA_Chapter14/src/com/yinhai/list_ 目录 项目代码 集合 一、引入 数组 集合 二、集合的框架体系 单列集合        双列集合        Collection类 一、Collection类接

    2024年02月06日
    浏览(54)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包