LinkeList底层实现原理及源码

LinkeList原理

LinkedList 和 ArrayList 一样,都实现了 List 接口,但其内部的数据结构有本质的不同。LinkedList 是基于链表实现的(通过名字也能区分开来),所以它的插入和删除操作比 ArrayList 更加高效。但也是由于其为基于链表的,所以随机访问的效率要比 ArrayList 差。

1.ArrayList在实现添加、插入底层都要进行扩容及位置偏移问题。删除要进行位置偏移,但是LinkeList 是不需要的。(所以ArrayList在这方面效率比LinkeList差)

2.LinkeList在做查询的时候,效率是非常低的。

链表讲解

链表分为:单链表和双链表

单向链表特性:

下图为单向链表的特性:
在这里插入图片描述
上一个节点会包含下一个节点的指向,假设:我要找到Node3节点,就需要通过Node1 找到 Node2 再找到Node3节点,不能直接找Node3节点,所以在查询的时候,性能是比较慢的。

双向链表的特性

在这里插入图片描述
当前这个节点会包含上一个节点和下一个节点的指向。(第一个节点是没有上一个节点的,所以不包含上一个)。

双向链表删除节点是怎样的?
会获取删除元素的上一个节点和下一个接点。

删除之前的图
在这里插入图片描述
删除之后的图
在这里插入图片描述
将Node3的上一个节点变为Node1
将Node1的下一个节点变为Node3

LinkeList源码分析

//实际存储大小
transient int size = 0;
//第一个元素
transient Node<E> first;
//最后一个元素
transient Node<E> last;

first和last解析:

在这里插入图片描述
图中链表中只存放一个节点Node1。那么first和last都是=Node1节点

在这里插入图片描述
图中链表中只存放2节点Node1和Node2,那么first是=Node1,last是=Node2。

为什么需要first元素?

first元素是为了查询,从哪里开始到哪里结束。

为什么需要last元素?

last是为了定位添加元素的时候从哪里开始。

总之就是 查询的时候从头开始,添加的时候从尾开始。下面源码可以看出。linkLast

public boolean add(E e) {
        linkLast(e);
        return true;
    }

Node节点定义

源码:

//一个节点包含上一个节点和下一个节点。
private static class Node<E> {
        E item; //泛型 元素obj
        Node<E> next; //下一个节点
        Node<E> prev;//上一个节点
		//构造方法
        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

LinkeList的默认构造函数:
源码
默认的构造函数没有做任何事情。

	// 默认的构造函数没有做任何事情。
  public LinkedList() {
    }

add(obj)解析:
源码:

  /**
     * Links e as last element.
     */
    void linkLast(E e) {
    	//获取最后一个节点
        final Node<E> l = last;
        //创建一个 新的节点,最后一个节点作为当前新节点的上一个几点
        final Node<E> newNode = new Node<>(l, e, null);
        //创建新的节点作为最末尾节点,用于添加的开始
        last = newNode;
        //判断最后一个节点是否为null,如果为null表示容器中没有任何元素,不为null,则把最后一个节点的下一个节点指向新创建的节点
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;//容器的长度
        modCount++;
    }

get(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;
            return x;
        } else {
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }

remove(index)解析:
源码

 public E remove(int index) {
        checkElementIndex(index);
        //先根据下标获取节点
        return unlink(node(index));
    }

//根据下标获取节点之后的操作

 E unlink(Node<E> x) {
       	//获取节点中的内容及改节点的上一个节点和下一个节点
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;
		//判断获取到的上一个节点是否为null,为null表示:删除的节点是第一个节点或者容器中只有一个节点。
        if (prev == null) {
            first = next;//下一个节点作为首节点
        } else {
        	//更新上一个节点的下一个节点指向
            prev.next = next;
            x.prev = null;//待删除的节点的上一个节点设为null,用于gc回收(不可达对象)
        }
		//判断获取到的下一个节点是否为null,为null表示:删除的节点是末尾节点或者容器中只有一个节点。
        if (next == null) {
            last = prev;//上一个节点作为末节点
        } else {
            next.prev = prev;
            x.next = null;
        }

        x.item = null;
        //长度-1
        size--;
        modCount++;
        return element;
    }

add(index,obj)解析:
原理图
在这里插入图片描述
根据下标添加数据,其实实现方式跟remove差不多。
源码:

 public void add(int index, E element) {
 		//检查下标是否越界及是否遵循标准
        checkPositionIndex(index);
		//
        if (index == size)
            linkLast(element);//直接走add()方法
        else
        	//先根据下标获取节点。
            linkBefore(element, node(index));
    }
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;//新插入的节点作为根据下标获取节点的上一个节点。
        //判断是否为第一个节点或者容器只有一个节点。
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        size++;
        modCount++;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ava实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),可运行高分资 Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。下面详细介绍C语言的基本概念和语法。 1. 变量和数据类型 在C语言中,变量用于存储数据,数据类型用于定义变量的类型和范围。C语言支持多种数据类型,包括基本数据类型(如int、float、char等)和复合数据类型(如结构体、联合等)。 2. 运算符 C语言中常用的运算符包括算术运算符(如+、、、/等)、关系运算符(如==、!=、、=、<、<=等)、逻辑运算符(如&&、||、!等)。此外,还有位运算符(如&、|、^等)和指针运算符(如、等)。 3. 控制结构 C语言中常用的控制结构包括if语句、循环语句(如for、while等)和switch语句。通过这些控制结构,可以实现程序的分支、循环和多路选择等功能。 4. 函数 函数是C语言中用于封装代码的单元,可以实现代码的复用和模块化。C语言中定义函数使用关键字“void”或返回值类型(如int、float等),并通过“{”和“}”括起来的代码块来实现函数的功能。 5. 指针 指针是C语言中用于存储变量地址的变量。通过指针,可以实现对内存的间接访问和修改。C语言中定义指针使用星号()符号,指向数组、字符串和结构体等数据结构时,还需要注意数组名和字符串常量的特殊性质。 6. 数组和字符串 数组是C语言中用于存储同类型数据的结构,可以通过索引访问和修改数组中的元素。字符串是C语言中用于存储文本数据的特殊类型,通常以字符串常量的形式出现,用双引号("...")括起来,末尾自动添加'\0'字符。 7. 结构体和联合 结构体和联合是C语言中用于存储不同类型数据的复合数据类型。结构体由多个成员组成,每个成员可以是不同的数据类型;联合由多个变量组成,它们共用同一块内存空间。通过结构体和联合,可以实现数据的封装和抽象。 8. 文件操作 C语言中通过文件操作函数(如fopen、fclose、fread、fwrite等)实现对文件的读写操作。文件操作函数通常返回文件指针,用于表示打开的文件。通过文件指针,可以进行文件的定位、读写等操作。 总之,C语言是一种功能强大、灵活高效的编程语言,广泛应用于各种领域。掌握C语言的基本语法和数据结构,可以为编程学习和实践打下坚实的基础。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值