LinkedList 源码解析(JDK1.8)

目录

一. 前言

二. 常用方法

三. 源码解析

3.1. 属性和内部类

3.2. 构造函数

3.3. 添加元素

 3.4. 获取元素

3.5. 删除元素

3.6. 迭代器

3.6.1. 头到尾方向的迭代

3.6.2. 尾到头方向的迭代

3.6.3. add() 插入元素

3.6.4. remove() 移除元素


一. 前言

    LinkedList同时实现了List接口和Deque接口,也就是说它既可以看作是一个顺序容器,又可以看作是一个队列(Queue),同时又可以看作一个栈(Stack)。这样看来,LinkedList简直就是个全能冠军。当你需要使用栈或者队列时,可以考虑使用LinkedList,一方面是因为Java官方已经声明不建议使用Stack类,更遗憾的是,Java里根本没有一个叫做Queue的类(它是个接口名字)。关于栈或队列,现在的首选是ArrayDeque,它有着比LinkedList(当作栈或队列使用时)有着更好的性能。

特点:
1. 底层是一个双向链表结构:增删快,查询慢。
2. 包含大量操作首尾元素的方法。
3. 线程不安全。如果需要多个线程并发访问,可以先采用Collections.synchronizedList()方法对其进行包装。

LinkedList 继承了 AbstractSequentialList,而 AbstractSequentialList 又继承于 AbstractList 。详见 ArrayList 的源码,阅读后我们知道,ArrayList 同样继承了 AbstractList , 所以LinkedList会有大部分方法和 ArrayList 相似。

简单介绍一下家族成员:
List:表明它是一个列表,支持添加、删除、查找等操作,并且可以通过下标进行访问。
Deque:继承自 Queue 接口,具有双端队列的特性,支持从两端插入和删除元素,方便实现栈和队列等数据结构。
Cloneable:表明它具有拷贝能力,可以进行深拷贝或浅拷贝操作。
Serializable:表明它可以进行序列化操作,也就是可以将对象转换为字节流进行持久化存储或网络传输,非常方便。
补充:
序列化是将对象转换成二进制流,持久化是将对象转换成介质数据(先转成二进制再持久化)。
序列化跟反序列化,发生在OSI的表示层。

二. 常用方法

方法描述
public E getFirst()返回此列表中的第一个元素。
public E getLast()返回此列表中的最后一个元素。
public E removeFirst()移除并返回此列表中的第一个元素。
public E removeLast()移除并返回此列表中的最后一个元素。
public void addFirst(E e)在此列表的开始处插入指定的元素。
public void addLast(E e)将指定的元素添加到列表的结束位置。
public boolean contains(Object o)返回 true如果这个列表包含指定元素。
public int size()返回此列表中元素的数目。
public boolean add(E e)将指定的元素添加到列表的结束位置。
public boolean remove(Object o)从该列表中移除指定元素的第一个。
public void clear()从此列表中移除所有的元素。
public E get(int index)返回此列表中指定位置的元素。
public E set(int index, E element)用指定元素替换此列表中指定位置的元素。
public void add(int index, E element)在列表中指定的位置上插入指定的元素。
public E remove(int index)移除此列表中指定位置的元素。所有后续元素左移(下标减1)。返回从列表中删除的元素。
public int indexOf(Object o)返回此列表中指定元素的第一个出现的索引,-1:如果此列表不包含元素。
public int lastIndexOf(Object o)返回此列表中指定元素的最后一个发生的索引,-1:如果此列表不包含元素。
public E peek()返回此列表的头部。
public E poll()返回并删除此列表的第一个元素。
public E remove()返回并删除此列表的第一个元素。
public boolean offer(E e)将指定的元素添加到列表的尾部(最后一个元素)。
public boolean offerFirst(E e)在列表的前面插入指定的元素。
public boolean offerLast(E e)在列表的结尾插入指定的元素。
public E peekFirst()返回列表的第一个元素。
public E peekLast()返回列表的最后一个元素。
public E pollFirst()删除并返回列表的第一个元素。
public E pollLast()删除并返回列表的最后一个元素。
public void push(E e)将一个元素推到由该列表所表示的堆栈上。换句话说,在这个列表的前面插入元素。相当于addFirst(E)。
public E pop()从该列表所表示的堆栈中弹出一个元素。换言之,移除并返回此列表的第一个元素。

三. 源码解析

3.1. 属性和内部类

/*** 集合大小* 当前有多少个节点*/
transient int size = 0;/*** 头部节点* Invariant: (first == null && last == null) ||*            (first.prev == null && first.item != null)*/
transient Node<E> first;/*** 尾部节点* Invariant: (first == null && last == null) ||*            (last.next == null && last.item != null)*/
transient Node<E> last;
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;}
}

3.2. 构造函数

LinkedList 中有一个无参构造函数和一个有参构造函数。

// 创建一个空的链表对象
public LinkedList() {
}// 接收一个集合类型作为参数,会创建一个与传入集合相同元素的链表对象
public LinkedList(Collection<? extends E> c) {this();addAll(c);
}

3.3. 添加元素

LinkedList 除了实现了List接口相关方法,还实现了 Deque 接口的很多方法,所以我们有很多种方式插入元素。我们这里以List接口中相关的插入方法为例进行源码讲解,对应的是 add()、addAll() 方法。
add(E e):用于在 LinkedList 的尾部插入元素,即将新元素作为链表的最后一个元素,时间复杂度为 O(1)。
add(int index, E element):用于在指定位置插入元素。这种插入方式需要先移动到指定位置,再修改指定节点的指针完成插入/删除,因此需要移动平均 n/2 个元素,时间复杂度为 O(n)。
addAll(int index, Collection<? extends E> c):从指定位置开始,将指定集合中的所有元素插入此列表。当不是构造方法调用时,它会在其最后节点追加新的元素。
offer(E e):内部调用 add(E e) 方法。
offerFirst(E e):内部调用 addFirst(E e) 方法。
offerLast(E e):内部调用 addLast(E e) 方法。
push(E e):内部调用addFirst方法实现

// 在链表尾部插入元素
public boolean add(E e) {linkLast(e);return true;
}// 在链表指定位置插入元素
public void add(int index, E element) {// 下标越界检查checkPositionIndex(index);// 判断 index 是不是链表尾部位置if (index == size)// 如果是就直接调用 linkLast 方法将元素节点插入链表尾部即可linkLast(element);else// 如果不是则调用 linkBefore 方法将其插入指定元素之前linkBefore(element, node(index));
}// 将元素节点插入到链表尾部
void linkLast(E e) {// 将最后一个元素赋值(引用传递)给节点 lfinal Node<E> l = last;// 创建节点,并指定节点前驱为链表尾节点 last,后继引用为空final Node<E> newNode = new Node<>(l, e, null);// 将 last 引用指向新节点last = newNode;// 判断尾节点是否为空// 如果 l 是 null,意味着这是第一次添加元素if (l == null)// 如果是第一次添加,将first赋值为新节点,此时链表只有一个元素first = newNode;else// 如果不是第一次添加,将新节点赋值给l(添加前的最后一个元素)的nextl.next = newNode;size++;modCount++;
}// 在指定元素之前插入元素
void linkBefore(E e, Node<E> succ) {// assert succ != null;断言 succ不为 null// 定义一个节点元素保存 succ 的 prev 引用,也就是它的前一节点信息final Node<E> pred = succ.prev;// 初始化节点,并指明前驱和后继节点final Node<E> newNode = new Node<>(pred, e, succ);// 将 succ 节点前驱引用 prev 指向新节点succ.prev = newNode;// 判断尾节点是否为空,为空表示当前链表还没有节点if (pred == null)first = newNode;else// succ 节点前驱的后继引用指向新节点pred.next = newNode;size++;modCount++;
}
/*** 从指定位置开始,将指定集合中的所有元素插入此列表。* 将当前位于该位置的元素(如果有)和任何后续元素向右移动(增加其索引)。* 新元素将按照指定集合的迭代器返回的顺序出现在列表中。** @param 插入指定集合中第一个元素的索引* @param c 包含要添加到此列表的元素的集合* @return {@code true} 如果此列表因调用而更改,则为true* @throws IndexOutOfBoundsException {@inheritDoc}* @throws NullPointerException if the specified collection is null*/
public boolean addAll(int index, Collection<? extends E> c) {// 检查index是否越界 判断是否是有效下标 此时index必须为0checkPositionIndex(index);Object[] a = c.toArray();// 数组lengthint numNew = a.length;if (numNew == 0)return false;// 创建两个双向链表节点 pred末游节点 succ首节点 初始值为nullNode<E> pred, succ;if (index == size) {// 初始首节点为nullsucc = null;// 存储第一个nodepred = last;} else {succ = node(index);pred = succ.prev;}// 此时开始创建双向链表进行链接for (Object o : a) {// 获取值@SuppressWarnings("unchecked") E e = (E) o;// 创建一个新的节点(上一个节点,当前数值,下一个节点)Node<E> newNode = new Node<>(pred, e, null);if (pred == null)// 第一个节点时 他的上一个节点时nullfirst = newNode; // 此时最后一个节点就是它本身else// 此时将上一个节点的下游节点链接就是当前生成的pred.next = newNode;// 记录当前末游节点pred = newNode; }// 此时数据链接完if (succ == null) {// 将最后一个节点赋值给lastlast = pred;// 如果存在首届} else {// 将最后一个的下游节点链接为第一个节点pred.next = succ;// 将第一个的上游节点链接为末游节点succ.prev = pred;}//size=length+1size += numNew;modCount++;return true;
}
public boolean offer(E e) {return add(e);
}public boolean offerFirst(E e) {addFirst(e);return true;
}public boolean offerLast(E e) {addLast(e);return true;
}public void push(E e) {addFirst(e);
}

 3.4. 获取元素

LinkedList获取元素相关的方法一共有 3 个:
getFirst():获取链表的第一个元素。
getLast():获取链表的最后一个元素。
get(int index):获取链表指定位置的元素。

// 获取链表的第一个元素
public E getFirst() {final Node<E> f = first;if (f == null)throw new NoSuchElementException();return f.item;
}// 获取链表的最后一个元素
public E getLast() {final Node<E> l = last;if (l == null)throw new NoSuchElementException();return l.item;
}// 获取链表指定位置的元素
public E get(int index) {// 下标检查checkElementIndex(index);// 返回链表中对应下标的元素return node(index).item;
}

添加元素时,都返回了node(int index)方法,我们分析一下该方法:

// 返回指定下标的非空节点
Node<E> node(int index) {// 断言下标未越界// assert isElementIndex(index);// 如果index小于size的二分之一  从前开始查找(向后查找),反之向前查找if (index < (size >> 1)) {Node<E> x = first;// 遍历,循环向后查找,直至 i == indexfor (int i = 0; i < index; i++)x = x.next;return x;} else {Node<E> x = last;//遍历,循环向前查找,直至 i == indexfor (int i = size - 1; i > index; i--)x = x.prev;return x;}
}

get(int index) 或 remove(int index) 等方法内部都调用了该方法来获取对应的节点。
不难看出,该方法通过比较索引值与链表 size 的一半大小来确定从链表头还是链表尾开始遍历。
1. 如果索引值小于 size 的一半,就从链表头开始遍历
2. 如果索引值大于 size 的一半,就从链表尾开始遍历。
这样可以在较短的时间内找到目标节点,充分利用了双向链表的特性来提高效率。

3.5. 删除元素

LinkedList 删除元素相关的方法一共有 5 个:
removeFirst() :删除并返回链表的第一个元素。
removeLast() :删除并返回链表的最后一个元素。
remove(E e) :删除链表中首次出现的指定元素,如果不存在该元素则返回 false。
remove(int index) :删除指定索引处的元素,并返回该元素的值。
void clear() :移除此链表中的所有元素。
E pop():调用removeFirst()。
E poll():判断first元素是否为空,不为空则调用unlinkFirst(E e)删除。
E pollFirst:判断first元素是否为空,不为空则调用unlinkFirst(E e)删除。
E pollLast:判断last元素是否为空,不为空则调用unlinkLast(E e)删除。

// 删除并返回链表的第一个元素
public E removeFirst() {final Node<E> f = first;if (f == null)throw new NoSuchElementException();return unlinkFirst(f);
}// 删除并返回链表的最后一个元素
public E removeLast() {final Node<E> l = last;if (l == null)throw new NoSuchElementException();return unlinkLast(l);
}// 删除链表中首次出现的指定元素,如果不存在该元素则返回 false
public boolean remove(Object o) {// 如果指定元素为 null,遍历链表找到第一个为 null 的元素进行删除if (o == null) {for (Node<E> x = first; x != null; x = x.next) {if (x.item == null) {unlink(x);return true;}}} else {// 如果不为 null ,遍历链表找到要删除的节点for (Node<E> x = first; x != null; x = x.next) {if (o.equals(x.item)) {unlink(x);return true;}}}return false;
}// 删除链表指定位置的元素
public E remove(int index) {// 下标越界检查,如果越界就抛异常checkElementIndex(index);return unlink(node(index));
}

我们可以看到,均调用了 unlink(Node<E> x),对该方法解析:

E unlink(Node<E> x) {// 断言 x 不为 null// assert x != null;// 获取当前节点(也就是待删除节点)的元素final E element = x.item;// 获取当前节点的下一个节点final Node<E> next = x.next;// 获取当前节点的前一个节点final Node<E> prev = x.prev;// 如果前一个节点为空,则说明当前节点是头节点if (prev == null) {// 直接让链表头指向当前节点的下一个节点first = next;} else { // 如果前一个节点不为空// 将前一个节点的 next 指针指向当前节点的下一个节点prev.next = next;// 将当前节点的 prev 指针置为 null,方便 GC 回收x.prev = null;}// 如果下一个节点为空,则说明当前节点是尾节点if (next == null) {// 直接让链表尾指向当前节点的前一个节点last = prev;} else { // 如果下一个节点不为空// 将下一个节点的 prev 指针指向当前节点的前一个节点next.prev = prev;// 将当前节点的 next 指针置为 null,方便 GC 回收x.next = null;}// 将当前节点元素置为 null,方便 GC 回收x.item = null;size--;modCount++;return element;
}

unlink() 方法的逻辑如下:
1. 首先获取待删除节点 x 的前驱和后继节点;
2. 判断待删除节点是否为头节点或尾节点:
    1>. 如果 x 是头节点,则将 first 指向 x 的后继节点 next。
    2>. 如果 x 是尾节点,则将 last 指向 x 的前驱节点 prev。
    3>. 如果 x 既不是头节点也不是尾节点,执行下一步操作。
3. 将待删除节点 x 的前驱的后继指向待删除节点的后继 next,断开 x 和 x.prev 之间的链接;
4. 将待删除节点 x 的后继的前驱指向待删除节点的前驱 prev,断开 x 和 x.next 之间的链接;
5. 将待删除节点 x 的元素置空,修改链表长度。

public E pop() {return removeFirst();
}public E poll() {// 判断first元素是否为空,不为空则调用unlinkFirst(E e)删除final Node<E> f = first;return (f == null) ? null : unlinkFirst(f);
}public E pollFirst() {// 判断first元素是否为空,不为空则调用unlinkFirst(E e)删除final Node<E> f = first;return (f == null) ? null : unlinkFirst(f);
}public E pollLast() {// 判断last元素是否为空,不为空则调用unlinkLast(E e)删除final Node<E> l = last;return (l == null) ? null : unlinkLast(l);
}

3.6. 迭代器

推荐使用 for-each 循环来遍历 LinkedList 中的元素,for-each 循环最终会转换成迭代器形式。

LinkedList 遍历的核心就是它的迭代器的实现。

// 双向迭代器
private class ListItr implements ListIterator<E> {// 表示上一次调用 next() 或 previous() 方法时经过的节点;private Node<E> lastReturned;// 表示下一个要遍历的节点;private Node<E> next;// 表示下一个要遍历的节点的下标,也就是当前节点的后继节点的下标;private int nextIndex;// 表示当前遍历期望的修改计数值,用于和 LinkedList 的 modCount 比较,判断链表是否被其他线程修改过。private int expectedModCount = modCount;…………
}

下面我们对迭代器 ListItr 中的核心方法进行详细介绍。

3.6.1. 头到尾方向的迭代

// 判断还有没有下一个节点
public boolean hasNext() {// 判断下一个节点的下标是否小于链表的大小,如果是则表示还有下一个元素可以遍历return nextIndex < size;
}
// 获取下一个节点
public E next() {// 检查在迭代过程中链表是否被修改过checkForComodification();// 判断是否还有下一个节点可以遍历,如果没有则抛出 NoSuchElementException 异常if (!hasNext())throw new NoSuchElementException();// 将 lastReturned 指向当前节点lastReturned = next;// 将 next 指向下一个节点next = next.next;nextIndex++;return lastReturned.item;
}

3.6.2. 尾到头方向的迭代

// 判断是否还有前一个节点
public boolean hasPrevious() {return nextIndex > 0;
}// 获取前一个节点
public E previous() {// 检查是否在迭代过程中链表被修改checkForComodification();// 如果没有前一个节点,则抛出异常if (!hasPrevious())throw new NoSuchElementException();// 将 lastReturned 和 next 指针指向上一个节点lastReturned = next = (next == null) ? last : next.prev;nextIndex--;return lastReturned.item;
}

3.6.3. add() 插入元素

LinkedList<String> list = new LinkedList<>();
list.add("apple");
list.add(null);
list.add("banana");//  Collection 接口的 removeIf 方法底层依然是基于迭代器
list.removeIf(Objects::isNull);for (String fruit : list) {System.out.println(fruit);
}

3.6.4. remove() 移除元素

// 从列表中删除上次被返回的元素
public void remove() {// 检查是否在迭代过程中链表被修改checkForComodification();// 如果上次返回的节点为空,则抛出异常if (lastReturned == null)throw new IllegalStateException();// 获取当前节点的下一个节点Node<E> lastNext = lastReturned.next;// 从链表中删除上次返回的节点unlink(lastReturned);// 修改指针if (next == lastReturned)next = lastNext;elsenextIndex--;// 将上次返回的节点引用置为 null,方便 GC 回收lastReturned = null;expectedModCount++;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/111859.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TS使用echarts柱状图鼠标放上去并弹出

效果 代码 <template><div><Chart style"width: 100%; height: 344px" :option"chartOption" /></div> </template><script lang"ts" setup>import { ref } from vue;import { ToolTipFormatterParams } f…

Keeplived安装部署(单机双机)

Keeplived官网&#xff1a;https://www.keepalived.org/download.html 一 单机安装配置: 1.上传keepalived安装包并且安装 [rootmaster1 local]# tar -zxvf keepalived-2.2.8.tar.gz [rootmaster1 local]# mv keepalived-2.2.8 keepalived [rootmaster1 local]# chown root:r…

智能井盖是什么?万宾科技智能井盖传感器有什么特点

智能井盖是一种基于物联网和人工智能技术的新型城市设施。它不仅具备传统井盖的功能&#xff0c;还能通过数字化、自动化的方式实现远程监控和智能管理&#xff0c;提升城市运行效率和服务水平。 WITBEE万宾智能井盖传感器EN100-C2是一款井盖异动监测的传感终端。对窨井盖状态(…

分享一下微信公众号抽奖活动怎么弄

微信公众号抽奖活动是一种非常有效的营销手段&#xff0c;可以吸引大量用户参与&#xff0c;提高品牌曝光度和用户粘性。下面将介绍如何策划和实施一个成功的微信公众号抽奖活动。 一、明确活动目的 首先&#xff0c;需要明确微信公众号抽奖活动的目的。通常&#xff0c;抽奖活…

同花顺动态Cookie反爬JS逆向分析

文章目录 1. 写在前面2. 请求分析3. Hook Cookie4. 补环境 1. 写在前面 最近有位朋友在大A失意&#xff0c;突发奇想自己闲来无事想要做一个小工具&#xff0c;监测一下市场行情的数据。自己再分析分析&#xff0c;虽是一名程序员但苦于对爬虫领域相关的技术不是特别熟悉。最后…

万宾科技智能井盖传感器特点介绍

当谈论城市基础设施的管理和安全时&#xff0c;井盖通常不是第一项引人注目的话题。然而&#xff0c;传统井盖和智能井盖传感器之间的差异已经引起了城市规划者和工程师的广泛关注。这两种技术在功能、管理、安全和成本等多个方面存在着显著的差异。 WITBEE万宾智能井盖传感器E…

bug记录——设置了feign的fallback,但是没有生效

问题描述 feign的代码 package com.tianju.order.feign;import com.tianju.order.feign.fallback.StorageFallback; import com.tinaju.common.dto.GoodsDto; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMap…

Edge---微软浏览器-兼容性问题-解决办法(详细)

图片现象&#xff1a; 快捷键&#xff1a;winR &#xff08;进入管理员命令窗口&#xff09; 输入&#xff1a;regedit &#xff08;进入注册表编辑器&#xff09; 点击文件夹&#xff1a;HKEY_LOCAL_MACHINE 找到这个路径的文件项&#xff1a;HKEY_LOCAL_MACHINE\SOFTWARE…

web前端面试-- IEEE754标准JS精度丢失问题0.1+0.2!=0.3、0.2+0.3==0.5 十进制转二进制讲解

本人是一个web前端开发工程师&#xff0c;主要是vue框架&#xff0c;整理了一些面试题&#xff0c;今后也会一直更新&#xff0c;有好题目的同学欢迎评论区分享 ;-&#xff09; web面试题专栏&#xff1a;点击此处 背景 WEB前端面试官&#xff1a;0.20.3是否等于0.5&#xff…

JDBC-day06(数据库连接池)

八&#xff1a;数据库连接池 1. 传统连接数据库的模式 在使用开发基于数据库的web程序时&#xff0c;传统的模式基本是按以下步骤&#xff1a; 在主程序&#xff08;如servlet、bea&#xff0c;ns&#xff09;中建立数据库连接进行sql操作断开数据库连接 传统的模式存在的问…

zookeeper(目前只有安装)

安装 流程 学kafka的时候安装 Apache ZooKeeper 安装地址&#xff1a;https://archive.apache.org/dist/zookeeper/zookeeper-3.5.7/apache-zookeeper-3.5.7-bin.tar.gz 解压 tar -zxvf kafka_2.12-3.0.0.tgz -C /export/server/ 改配置 cd config cp zoo_sample.cfg z…

Pixhawk 6c (PX4)真机USB连接mavros失败

概述 笔者和同事近期购买了Pixhawk6c&#xff0c;它也是目前Pixhawk系列中最新的飞控。 但是在测试的过程中遇到了一个问题&#xff0c;发现它无法连接到mavros。 于是进行了一系列原因摸排&#xff0c;在国内的博客尚未看到能用的解决方案&#xff0c;在px4论坛上倒是找到了答…

VUE:侧边弹出栏组件,组件中有树状图,搜索框可筛选树状图节点,可收缩

作者:CSDN @ _乐多_ 本文记录了一个侧边弹出栏组件代码。代码即插即用。 弹出栏中有树状图,搜索框,可收缩。 其中,搜索框可筛选树状图节点。点击右侧小按钮可以收缩弹出框,点击X号也可以收缩弹出框。 文章目录 一、组件代码代码依赖element-plus库。且需要下载几个svg图…

grafana v10.1版本设置告警

1. 相关概念概述 如图所示&#xff0c;点击切换菜单标志&#xff0c;可以看到警报相关子选项。 警报规则&#xff1a;通过PromQL语句定义告警规则&#xff0c;即达到怎样的状态触发告警。 联络点&#xff1a; 设置当警报规则实例触发时&#xff0c;如何通知联系人&#xff0c;…

AI杀疯!2023上半年至今有趣的AI算法(内附视频)

公众号&#xff1a;算法一只狗 文章目录 第一个&#xff0c;一切都可以进行分割第二个&#xff0c;开源图文回答工具第三个&#xff0c;视频转换风格生成第四个&#xff0c;免费好用的文档对话工具文档对话能力文档联系功能 今年&#xff0c;我们见证了人工智能算法的起飞&…

Python 机器学习入门之ID3决策树算法

系列文章目录 第一章 Python 机器学习入门之线性回归 第一章 Python 机器学习入门之梯度下降法 第一章 Python 机器学习入门之牛顿法 第二章 Python 机器学习入门之逻辑回归 番外 Python 机器学习入门之K近邻算法 番外 Python 机器学习入门之K-Means聚类算法 第三章 Python 机…

python知识:从PDF 提取文本

一、说明 PDF 到文本提取是自然语言处理和数据分析中的一项基本任务&#xff0c;它允许研究人员和数据分析师从 PDF 文件中包含的非结构化文本数据中获得见解。Python 是一种通用且广泛使用的编程语言&#xff0c;它提供了多个库和工具来促进提取过程。 二、各种PDF操作库 让我…

Vue+openlayers+projs4实现坐标转换

一、背景 有一堆点数据&#xff0c;需要在地图上标记&#xff0c;只知参考北京54坐标系或西安80坐标系&#xff0c;但具体是哪种不清楚&#xff0c;这时候就需要坐标转换。ps&#xff1a;EPSG&#xff1a;3857&#xff08;openlayers参照的坐标系&#xff09; 二、思路 1、研…

Jmeter接口测试:jmeter导入和导出接口的处理

JMeter测试导入接口 利用Jmeter测试上传文件&#xff0c;首先可根据接口文档或者fiddler抓包分析文件上传的接口&#xff1b;如下图&#xff1a; 以下是我通过fiddler所截取的文件上传的接口 1、填写导入接口的信息 查看文件上传栏下的填写信息&#xff1a; 文件名称&#x…

大数据Flink(九十九):SQL 函数的解析顺序和系统内置函数

文章目录 SQL 函数的解析顺序和系统内置函数 一、​​​​​​​SQL 函数