Vector集合源码分析
Vector集合源码分析
文章目录
- Vector集合源码分析
- 一、字段分析
- 二、方法分析
- 三、总结
内容如有错误或者其他需要注意的知识点,欢迎指正或者探讨补充,共同进步。
一、字段分析
//用于存储该集合中的所有数据对象,所以是基于数组实现的
protected Object[] elementData;//用于记录当前集合中的数据量
protected int elementCount;//在进行扩容操作时,每次扩容的大小。如果为0,则按每次两倍的容量进行扩容(即增加当前容量的一倍)
protected int capacityIncrement;/*
**该字段继承自父类AbstractList,用于记录对列表结构进行修改的次数。
**当通过迭代器遍历列表时,迭代器会记录当前迭代的列表结构版本号,即初始时的 modCount 值。
**每次调用 next() 方法时,迭代器都会检查当前的 modCount 值是否与初始时的版本号一致,如果不一致
**则说明列表在迭代期间被修改过,就会抛出 ConcurrentModificationException 异常。
**使用 modCount 可以在迭代过程中及时检测到列表结构的修改,从而确保迭代器遍历的安全性和可靠性。
*/
protected transient int modCount = 0;
二、方法分析
-
添加元素 方法,是线程安全的方法。
//向已插入元素的后一位插入元素 public synchronized boolean add(E e) {//记录当前迭代的列表结构版本号,防止迭代过程中被别人也操作修改了,导致非线程安全问题。modCount++;//判断是否需要扩容ensureCapacityHelper(elementCount + 1);elementData[elementCount++] = e;return true;}//这时候的minCapacity 值为当前元素数量 + 1 private void ensureCapacityHelper(int minCapacity) {// 判断当前需要的容量大小 是否大于 数组的大小,大则需要扩容if (minCapacity - elementData.length > 0)grow(minCapacity);}//当前 minCapacity = 当前元素数量 + 1 即 elementcount + 1 private void grow(int minCapacity) {// overflow-conscious codeint oldCapacity = elementData.length;//这一步很关键,我们在Vector初始化的时候如果传入了参数capacityIncrement 且大于0,//则在原先的容量基础上+capacityIncrement ,如果没传或者为0,则扩容为原先数组容量的两倍。int newCapacity = oldCapacity + ((capacityIncrement > 0) ?capacityIncrement : oldCapacity);//如果扩容后溢出了,则就只在原先容量上+1,其实还是有溢出的可能,所以我们操作该集合时,要注意该可能性 if (newCapacity - minCapacity < 0)newCapacity = minCapacity;//这是后虽然还没溢出,但也几乎要溢出了,因为 MAX_ARRAY_SIZE 值为Integer.MAX_VALUE - 8,如果比MAX_ARRAY_SIZE 还打,则扩容为Integer.MAX_VALUE,否则为MAX_ARRAY_SIZEif (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);//用扩容后的容量创建新的数组,并将原数组数据迁移过来。//1。如果扩容后的数组塞不满之,后面补充null,或基本数据类型的默认值,比如int 默认0。//2。如果新的容量比原型容量还行,则进行阶段。elementData = Arrays.copyOf(elementData, newCapacity);}private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflowthrow new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;}//向数组的指定位置插入元素 public void add(int index, E element) {insertElementAt(element, index);} //同样是线程安全的 public synchronized void insertElementAt(E obj, int index) {modCount++;if (index > elementCount) {throw new ArrayIndexOutOfBoundsException(index+ " > " + elementCount);}//确定是否扩容,上面分析过了ensureCapacityHelper(elementCount + 1);//生成扩容后的新数组System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);elementData[index] = obj;//每次添加元素+1,这个值就是实际的元素数量,我们调用集合类的size()方法时,返回的就是这个值。elementCount++;}
-
获取元素 方法:根据索引获取值
public synchronized E get(int index) {//越界了if (index >= elementCount)throw new ArrayIndexOutOfBoundsException(index);//就是返回该数组下标位置的元素,没啥好说的。return elementData(index);}@SuppressWarnings("unchecked") E elementData(int index) {return (E) elementData[index]; }
-
扩容方法:介绍添加元素方法时说过了。
-
移除元素方法:可以根据下标位置移除元素,也可更具元素值移除元素。
//根据元素值删除元素 public boolean remove(Object o) {return removeElement(o);} //同样是线程安全的 public synchronized boolean removeElement(Object obj) {// 修改版本号,防止正在被迭代,导致非线程安全问题。modCount++;//确定元素下下标位置int i = indexOf(obj);if (i >= 0) {removeElementAt(i);return true;}return false;} //确定元素o下标位置 public int indexOf(Object o) {//为啥传0呢,因为要在一个数组里面找指定值的元素,当然是从该数组下标0开始往后遍历啦。return indexOf(o, 0);}//从下标index开始,查找元素o public synchronized int indexOf(Object o, int index) {//从这里也可看出,Vector 集合支持null的存储的。//同时我们也可看出,如果集合中存在值相同(基本数据类型),或者同一对象(非基本类型),会将靠索引小的删除掉,而后面的不会删除。这点操作时要注意,比如我们往 Vector<Integer> 添加两个形同对象,在掉remove(该对象),只能删除一个,如下图测试所示。if (o == null) {for (int i = index ; i < elementCount ; i++)if (elementData[i]==null)return i;} else {for (int i = index ; i < eleme0ntCount ; i++)if (o.equals(elementData[i]))return i;}return -1;}//获取到元素下标后,更具下标移除元素 public synchronized void removeElementAt(int index) {//生成新的版本号modCount++;//越界if (index >= elementCount) {throw new ArrayIndexOutOfBoundsException(index + " >= " +elementCount);}//越界else if (index < 0) {throw new ArrayIndexOutOfBoundsException(index);}//计算要删除元素后面还有多少个元素int j = elementCount - index - 1;if (j > 0) {//将元素通过赋值前移System.arraycopy(elementData, index + 1, elementData, index, j);}//元素数量-1elementCount--;//如果是最后一个元素,或是是已经调用 System.arraycopy迁移后的数组,则将最后以为设为nullelementData[elementCount] = null; /* to let gc do its work */}//根据索引位置删除元素 public synchronized E remove(int index) {//版本号+1modCount++;//判断是都越界if (index >= elementCount)throw new ArrayIndexOutOfBoundsException(index);//获取要删除的元素值 E oldValue = elementData(index);//需要移动的元素数量,就是你讲index位置的元素删了,那这个元素后院的所有的元素都要前移,这个值就是需要//移动的元素数量即index后面元素的数量int numMoved = elementCount - index - 1;//判断index后面是否有值if (numMoved > 0)//将要移动的元素前移,其实是吧那个要删除的元素覆盖了从而达到了删除元素的目的。System.arraycopy(elementData, index+1, elementData, index,numMoved);为什么要删除末尾元素呢,因为如果index 是最后以为元素,那么就不会触发元素前移,所以这里//多一步,将末尾元素设置为null,就是为了处理这一特殊情况。所以如果index 后面有值,这一步是//多余的,末尾已经是null了。 elementData[--elementCount] = null; // Let gc do its work//返回被删除的元素元素值return oldValue; }
-
迭代设计:内部类,实现了 Iterator。
- 但是Vector 提供了两种内部类用来实现 Iterator,分别是 Itr 和 ListItr,两种有所区别,使用场景也有所不同
- Itr:实现基本的迭代功能,通过维护一个 游标cursor 来遍历Vector的元素,不支持迭代过程中对Vector进行修改(增加或者删除元素),否则会抛出ConcurrentModificationException 异常。
- 适用于只需要遍历访问Vector元素而不进行修改的场景。
public synchronized Iterator<E> iterator() {return new Itr();}
private class Itr implements Iterator<E> {//游标,记录元素访问的位置,需要注意的是,游标记录的是下一次会访问的元素位置,而不是最后已经访问过的元素,比如://初始化时,游标值是0,调用next是,由于游标是0,所以会返回0下标位置的元素,然后游标+1,即下一次元素访问下标。int cursor; // index of next element to return//记录上一次已经访问的元素的位置,比如我们这次访问下标1,则lastRet 记录1,而cursor会记录2(即下一次回访问的位置)。int lastRet = -1; // index of last element returned; -1 if no such//记录版本号,记录在初始化迭代器时,当时的Vector的版本号。//前面也介绍过,在对Vector集合进行添加或者删除元素时,会执行 modCount ++;//在迭代的过程中,或比对 modCount 和 expectedModCount 的值,//从而判断迭代过程中Vector集合是否被修改过,修改过则抛出ConcurrentModificationException 异常。int expectedModCount = modCount;//判断下一步迭代是否还有元素,很简单,就是用当前迭代的位置(即游标的值) 和 元素的数量比较即可。public boolean hasNext() {// Racy but within spec, since modifications are checked// within or after synchronization in next/previousreturn cursor != elementCount;}//获取下一个元素,线程安全的,锁的是Vector.class 对象,保证在创建多个迭代器对象时,也是线程安全的。public E next() {synchronized (Vector.this) {//就是判断是Vector集合是否被修改过,就是用的版本号modCount 和 初始化时记录的 expectedModCount 比较,//判断是否发生变化,变化了则被修改过。下边也贴出了源码,很简单。checkForComodification();//当前要访问的元素位置,呼应前面讲的,cursor记录的是要访问元素的位置。int i = cursor;//如果说当前位置是最后一个元素位置了,即后面没值了,会抛出异常,这也告诉我们,在调用next()方法是,最好想用//hasNext()判断下,防止一直掉next(),遍历到末尾了从而抛出异常。下面贴出了测试情况。if (i >= elementCount)throw new NoSuchElementException();//游标+1,即下一次要访问元素的位置。cursor = i + 1;//记录本次访问的元素下标位置,并返回该位置的元素。return elementData(lastRet = i);}}//移除元素,通过代码可以知道,这里有一个细节需要注意:移除的并不是最末尾的那个元素,而是我们已经遍历过的//所有元素的末尾那个元素。比如用来存储数据的数组elementData[5],有五个元素,而我们在调用next()迭代访问元素的//过程中,访问到了index = 2,则游标cursor = 3,lastRef = 2,那么我们调用remove时,//其实删除的是index = 2位置的元素,无图说。。下面贴出来了测试结果。public void remove() {//判断参数是否异常if (lastRet == -1)throw new IllegalStateException();synchronized (Vector.this) {//判断迭代过程中Vector是否被修改过checkForComodification();//删除 lastRec 下标位置的元素,该方法上面分析过了,删除之后,后面的元素前移。Vector.this.remove(lastRet);//更新版本号,这里有点奇怪,按理说我们初始化开始时版本号是多少,我们在操作迭代器的整个过程中,//版本应该是不能改变的,所以这里按理说应该是一直相等的,为啥要重新赋值呢???//所以也可以看出,Vector 的删除过程并不是100% 保证集合不被修改的。//即在checkForComodification()执行完之后正好对集合发生修改,不会抛出错误,而且迭代器也不会抛出异常。//这里和next()迭代不同了,next()要求整个迭代过程都不能修改Vector集合。//个人思考这里为啥这样:// 可能和方法的目的不同有关,next()迭代目的是获取所有元素,我们希望获取创建迭代器那一时刻的元素,// 关心的是元素的值,肯定不希望我获取这些元素过程中还有别人来修改。// 而remove()只是删除,对于元素值并不关心的。只要达到删除的目的即可。// 所以因为这里,也可能导致next()获取的值和我们刚刚开始创建迭代器时集合的值发生变化。如何做呢?// 我们先调用next()获取值,在调用remove删除,并且删除时好巧不巧在checkForComodification()// 执行后,修改了集合,后面在调用next()也不会报错,正常迭代,但是集合已经被修改了。。。//总之:该迭代器并不能100%保证迭代过程中,集合不被修改的(即调用next,又调用remove),并且不抛出异常。//但是如果只调用next()确实可以保证。expectedModCount = modCount;}//更新游标,因为后面元素都往前移了cursor = lastRet;//可以看出迭代器不能连续调用两次,否则抛出异常lastRet = -1;}//函数接口作为参数,可以在遍历过程中,执行该函数@Overridepublic void forEachRemaining(Consumer<? super E> action) {//不能为空Objects.requireNonNull(action);synchronized (Vector.this) {final int size = elementCount;int i = cursor;if (i >= size) {return;}@SuppressWarnings("unchecked")final E[] elementData = (E[]) Vector.this.elementData;if (i >= elementData.length) {throw new ConcurrentModificationException();}//遍历参数,让每一个元素都执行action的方法,同时保证执行过程中,集合不能被修改while (i != size && modCount == expectedModCount) {action.accept(elementData[i++]);}// update once at end of iteration to reduce heap write traffic//更新游标到末尾,其实就是遍历完了,五元素可编列。//举个例子:比如elementData[5],这时,cursor,就是5,lastRef = 4;cursor = i;lastRet = i - 1;//最后再次检查集合是否被修改过。checkForComodification();}}//检查集合版本和迭代器版本是相同,从而判断迭代过程中,集合有没有被修改。final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}}//判断迭代过程中,Vector集合是否被修改,修改了则抛出 ConcurrentModificationException 异常。
final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}
//返回该索引位置的元素
E elementData(int index) {return (E) elementData[index];}
- ListItr:继承自Itr,实现了ListIterator。
- 相比Itr,提供了额外的方法功能。并且支持在迭代的过程中对 Vector进行修改(增加、删除元素),不会抛出ConcurrentModificationException异常。
- 通过维护一个 游标cursor 和一个最后访问元素的索引 lastRet 来遍历和修改 Vector 中的元素。
- 适用于访问 Vector 元素并且对其进行修改的场景。
final class ListItr extends Itr implements ListIterator<E> {//初始化迭代器,这里和Itr迭代器不同。//Itr迭代只能从头开始迭代//而ListItr可以从指定位置开始迭代ListItr(int index) {super();cursor = index;}//判断当前游标位置的前面是否有值public boolean hasPrevious() {return cursor != 0;}//获取游标位置public int nextIndex() {return cursor;}//获取游标位置的前一个值,其实就是Itr的LastRef,即上一次已经遍历过的值。public int previousIndex() {return cursor - 1;}//获取上一次已经遍历过得值,其实就是前遍历。public E previous() {synchronized (Vector.this) {checkForComodification();//要获取的元素下标int i = cursor - 1;if (i < 0)throw new NoSuchElementException();//更新游标,即下一次访问元素下标。cursor = i;//返回要访问的元素,这里为什么要last = i呢??在Itr迭代器中,我们知道last 都是 = cur-1的,但是这里//lastRef = cursor了。我们只要记住他们的含义就好理解了,cursor记录的是下一个要访问元素的下标(正向遍历)//而lastRet记录的上一次访问的元素下标,因为这次我们访问的是cursor - 1,所以更新lastRet = i;return elementData(lastRet = i);}}//将上一次访问的元素用e给替换掉。public void set(E e) {if (lastRet == -1)throw new IllegalStateException();synchronized (Vector.this) {checkForComodification();Vector.this.set(lastRet, e);}}//向集合中添加元素,添加位置为cursor位置,需要注意并不是在数组末尾添加的,如果cursor后面还有值,会往后移动。//比如 elementData[5]{0,1,2,3,4},cursor = 3,调用add(8),则变成{0,1,2,8,3,4}public void add(E e) {int i = cursor;synchronized (Vector.this) {checkForComodification();//添加元素,并修改集合版本号Vector.this.add(i, e);//更新版本号expectedModCount = modCount;}cursor = i + 1;//这次是添加元素,所以没有对元素访问,即为-1lastRet = -1;}}
三、总结
- Vector:是线程安全的集合。因为添加元素和删除元素的方法都有syncnized修饰。因为syncnized是重量级锁且是悲观锁,所以效率并不高。
- 使用数组来存储元素。
- Vector有两种迭代器,可根据不同场景选择不同的迭代器。通过维护游标curosr和lastRet来维护下一个要访问的元素下标和上一次访问元素下标。
- Itr迭代器迭代过程不允许集合被修改,通过该迭代器初始化时记录集合的版本号,并在跌单的过程用该版本号和集合的版本号进行比对来判断是否被修改。但其实在遍历的过程中如果调用迭代器的remove()方法,可能会导致集合被修改,而又不触发异常的风险,详细可看上面的分析。
- ListItr迭代器提供了遍历过程中修改元素的方法,并且更新集合版本号和迭代器版本号,并使其一致。
相关文章:

Vector集合源码分析
Vector集合源码分析 文章目录 Vector集合源码分析一、字段分析二、方法分析三、总结 内容如有错误或者其他需要注意的知识点,欢迎指正或者探讨补充,共同进步。 一、字段分析 //用于存储该集合中的所有数据对象,所以是基于数组实现的 protec…...
Unity引擎中光源都有哪几种,都有什么作用
本文由 简悦 SimpRead 转码, 原文地址 mp.weixin.qq.com Unity 引擎为了实现游戏场景的明暗和光影效果,提供了四种类型的光源,分别是方向光(Directional Lights)、点光源(Point Lights)、聚光灯…...
C语言中结构体成员访问操作符的含义及其用法
1.直接访问操作符 用法:结构体名.成员名。 含义:直接访问结构体中的成员变量。 示例: #include<stdio.h> struct student {char name[20];int age; }; int main() {//定义了一个结构体数组arrstruct student arr[4] { {"cxk&q…...
Kubeadmin方式部署Calico网络模式的K8s集群
目录 1.环境准备 2.配置内核参数 3.配置ntp时间服务器 4.配置持久化日志目录 5.升级物理机内核 6.配置ipvs服务 7.安装docker 8.安装kubeadm、kubectl、kubelet 9.导入k8s组件基础镜像 10.k8s初始化配置 11.配置calico网络 12.完成部署 1.环境准备 ###方案中涉及的…...

sparse transformer 常见稀疏注意力
参考: https://zhuanlan.zhihu.com/p/259591644 主要就是降低transformer自注意力模块的复杂度 复杂度主要就是 Q K^T影响的,稀疏注意力就是在Q点乘K的转置这模块做文章 下列式一些sparse transformer稀疏注意力方法 a、transformer原始的 ࿰…...

力扣 第 125 场双周赛 解题报告 | 珂学家 | 树形DP + 组合数学
前言 整体评价 T4感觉有简单的方法,无奈树形DP一条路上走到黑了,这场还是有难度的。 T1. 超过阈值的最少操作数 I 思路: 模拟 class Solution {public int minOperations(int[] nums, int k) {return (int)Arrays.stream(nums).filter(x -> x <…...

基于springboot+vue的人格障碍诊断系统
博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战,欢迎高校老师\讲师\同行交流合作 主要内容:毕业设计(Javaweb项目|小程序|Pyt…...

Go-知识struct
Go-知识struct 1. struct 的定义1.1 定义字段1.2 定义方法 2. struct的复用3. 方法受体4. 字段标签4.1 Tag是Struct的一部分4.2 Tag 的约定4.3 Tag 的获取 githupio地址:https://a18792721831.github.io/ 1. struct 的定义 Go 语言的struct与Java中的class类似&am…...

SpringMVC 学习(十一)之数据校验
目录 1 数据校验介绍 2 普通校验 3 分组校验 4 参考文档 1 数据校验介绍 在实际的项目中,一般会有两种校验数据的方式:客户端校验和服务端校验 客户端校验:这种校验一般是在前端页面使用 JS 代码进行校验,主要是验证输入数据…...

软考55-上午题-【数据库】-数据库设计步骤1
一、数据库设计的步骤 新奥尔良法,四个主要阶段: 1、用户需求分析:手机用户需求,确定系统边界; 2、概念设计(概念结构设计):是抽象概念模型,较理想的是采用E-R方法。 …...
速盾:使用cdn后速度慢是怎么回事?
CDN(内容分发网络)是一种通过将网站的静态内容分布到全球各地的服务器,从而提供更快速度和更好用户体验的技术。然而,有时候用户会遇到使用CDN后速度变慢的问题,下面将探讨几种可能的原因。 服务器选择错误: CDN服务通…...

考研复试类比社团招新,无所谓“公平”,导师选谁都是他的权力
这篇文章是抖音和b站上上传的同名视频的原文稿件,感兴趣的csdn用户可以关注我的抖音和b站账号(GeekPower极客力量)。同时这篇文章也为视频观众提供方便,可以更加冷静地分析和思考。文章同时在知乎发表。 我考研一战的时候计算机考…...
阿里面试,有点焦虑。。
恭喜发现宝藏!搜索公众号【TechGuide】回复公司名,解锁更多新鲜好文和互联网大厂的笔经面经,目前已更新至美团、字节… 作者TechGuide【全网同名】 聊聊春招 春招来了,有些24届校招生可能还在做最后的努力,有些25届的…...
24计算机考研调剂 | 石家庄铁道大学
01石家庄铁道大学 智慧交通研究室招少量调剂学术型硕士(数一英一320分以上工科专业) 考研调剂招生信息 学校:石家庄铁道大学 专业:工学->计算机科学与技术->计算机应用技术 工学->测绘科学与技术->地图制图学与地理信息工程 工学->交…...

勇敢尝鲜之Springboot3大坑-集成Mybatisplus报错:ddlApplicationRunner
🌹作者主页:青花锁 🌹简介:Java领域优质创作者🏆、Java微服务架构公号作者😄 🌹简历模板、学习资料、面试题库、技术互助 🌹文末获取联系方式 📝 往期热门专栏回顾 专栏…...

linux高级编程:线程(二)、进程间的通信方式
线程: 回顾线程(一): 1.线程间通信问题 线程间共享同一个资源(临界资源) 互斥: 排他性访问 linux系统 -- 提供了Posix标准的函数库 -- 互斥量(互斥锁) 原子操作&#x…...

Unity 佳能SDK 及数据获取
1. 填写信息跟官方申请SDK,大概1-2个工作日会邮件回复你 佳能(中国)- 佳定制(佳能影像产品),SDK,EDSDK,CCAPI,软件开发包下载 2. 将SDK这两个文件放到 Unity Plugins文件夹 3. 把CameraControl 下面只要是绿色的 .cs 文件都复制到Unity 中...

Unity(第二十三部)导航
你可以使用 unity官方提供的 unity导航组件或第三方 unity导航组件,以实现游戏中角色或其他物体的导航。 unity导航组件通常具有多种导航模式,如飞行模式、步行模式、车辆模式等,可以根据不同的需求选择合适的模式。同时,unity导…...
根据建表sql语句生成go的struct代码工具
sql2struct 一个根据"CREATE TABLE"建表语句生成对应的Go语言结构体的工具,暂只支持 MySQL 表。 开发目的 在 github 中找到一些 sql2struct,但要么是 chrome 插件,要么是在线工具,要么是需要连接 MySQL,…...

Qt 自定义长条进度条(类似播放器进度条)
1.运行界面 2.步骤 其实很简单。 2.1绘制底图圆角矩形 2.2绘制播放进度圆角矩形 参考:painter绘图 3.源码 #pragma once#include <QWidget> #include <QLabel> #include <QHBoxLayout> #include <QMouseEvent> #include <QDebug&g…...

Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

剑指offer20_链表中环的入口节点
链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...

Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
Matlab | matlab常用命令总结
常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...