Java开发者の模型召唤术:LangChain4j咏唱指南(三)
Java开发者の模型召唤术:LangChain4j咏唱指南(三)
往期回顾:
- Java开发者の模型召唤术:LangChain4j咏唱指南(一)
- Java开发者の模型召唤术:LangChain4j咏唱指南(二)
上两期博客中简单的为大家介绍了
- langchain4j是什么、java 集成 langchain4j、集成阿里云百炼平台的各个模型以及本地安装的ollama模型的调用流程
- langchain4jSpring-boot集成、流式输出、记忆对话、function-call、预设角色等更深层次的知识
那么本期教程将会与大家分享关于langchain4j中的向量、向量存储、模型外接知识库相关的知识,码字不易,各位喜欢请一键三连~
1.RAG介绍
虽然对于我们上一期实现的模型来说,可以根据一些@Tools,@SystemMessage,对大模型的回答做一些限制,但是如果大模型需要在一些专业的领域回答问题,也不能通篇@Tools去实现专业领域的问题。因此往往会给大模型外接一个知识库。
1.1什么是RAG
RAG(Retrieval-Augmented Generation,检索增强生成)是一种结合信息检索与文本生成的先进AI技术,旨在通过引入外部知识库,提升生成内容的准确性和相关性。
1.2核心原理
检索(Retrieval)
- 当收到用户输入(如问题)时,RAG模型会从外部知识库(如文档、数据库)中检索相关段落或信息。
- 例如,若用户问“量子计算机的原理是什么?”,模型会先检索与“量子计算”相关的权威资料。
生成(Generation)
- 基于检索到的信息,生成模型(如GPT)会综合上下文,生成自然语言的回答。
- 生成过程不仅依赖模型本身的参数,还结合了检索到的实时数据。
1.3与传统模型的区别
传统模型(如GPT) | RAG |
---|---|
仅依赖训练时学到的静态知识 | 动态引入外部最新知识 |
可能生成“看似合理但不准确”的内容 | 生成结果更可靠、有据可依 |
无法直接引用具体文档 | 可追溯答案来源(如引用某篇论文) |
1.4应用场景
智能问答系统
- 如客服机器人,直接基于企业文档库生成精准回答。
学术研究助手
- 根据论文数据库,解释复杂概念或提供研究建议。
事实核查
- 快速检索权威信息,验证生成内容的真实性。
个性化推荐
- 结合用户历史数据,生成定制化建议(如医疗诊断辅助)。
1.5优略分析
- 优势:
- 解决“模型幻觉”(生成虚构内容)问题。
- 支持知识更新,无需重新训练模型即可扩展知识库。
- 挑战:
- 检索效率影响实时性(需优化检索算法)。
- 对知识库质量依赖高(垃圾数据会导致错误生成)。
比如:在一次与大模型的讯问中,可能是按照下面的步骤进行运作
那再进行RAG学习之前,有几个概念,我们在一起了解一下
1.6一些名词解释
在 RAG(检索增强生成) 框架中,向量(Vector) 是连接检索与生成的核心桥梁。通过将文本转化为高维向量,RAG 能够快速从海量知识库中检索相关信息,并基于检索结果生成更准确的回答。
向量通常用来做相似度的搜索,一般可以根据维度等分为一维,二维,多维等。
1.6.1一维向量
一维向量可以表示一些词汇的语义相似度,举个例子,对于"你好",“hello”,"很高兴认识你"这些词的语义大概是差不多的,可以在一维向量中做如下表示:
只是我自己的一点小理解,不一定全对,大家领会精神就行哈。
1.6.2二维/多维向量
那么对于一些比较复杂的对象,比如狗狗,简单的一个维度明显已经不够标志他们,可能得提取多个特征值才能比较准确的描述他们。
举个🌰:
对于一只狗来说,可能存在以下特征值:
- 颜色?
- 毛发?直毛还是卷毛
- 具体的犬种?
- …
那么这些特征值 [黑色,卷毛,小型泰迪,…] 便可以表示出一种狗狗
如果需要检索更加精准,我们肯定还需要更多维度的向量,更加确定的将一个物体抽象成一组数据,组成更多维度的空间,在多维向量空间中,相似性检索变得更加复杂。
可能需要使用一些算法,如余弦相似度或欧几里得距离,来计算向量之间的相似性。
但是在langchain4j中会通过向量数据库会帮我们实现。
1.7文本向量化
langchain4j中可以使用文本向量化的方式,实现对于一句话的向量化,并通过相关的向量数据库存储;先来了解一下文本的向量化,具体实现方式如下:
import dev.langchain4j.community.model.dashscope.QwenEmbeddingModel;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.model.output.Response;/*** @version 1.0* @Author jerryLau* @Date 2025/3/31 8:35* @注释 简单的向量化示例*/
public class EmBeddingExample {public static void main(String[] args) {QwenEmbeddingModel build = QwenEmbeddingModel.builder().apiKey("sk-XXXXXX").build();String text = "我是一个测试文本";Response<Embedding> embed = build.embed(text);System.out.println(embed.content().toString());System.out.println(embed.content().vectorAsList().size());}
}
代码执行结果:
可以看到“我是一个测试文本”,这句话在QwenEmbeddingModel的向量化之后,生成了一个长度为1536的向量数组,由于是使用的QwenEmbeddingModel进行的向量化,所以这个长度是固定的,不论向量化的文本长度为多少,经向量化后,都会生成长度为1536的向量数组,即向量化结果输出长度固定,不会随被向量化文本长度变化。
那么计算出这个向量后有什么用呢?在举个🌰
比如你要查询一些资料,输入了你想查的问题,在将你的问题向量化后,得到一组数值;获取到这个向量数组后,在向量数据库中寻找到相似度最高的文本资料,返回给你,即你会得到你问题的答案。
1.8向量数据库
1.8.1langchain4j支持的向量数据库
对于向量模型生成出来的向量,我们可以持久化到向量数据库,并且能利用向量数据库来计算两个向量之间的相似度,并且根据相似度找到某个向量最相似的向量。
在LangChain4j中,EmbeddingStore表示向量数据库,它有支持20+嵌入模型:
其中我们日常使用中的一些数据库,比如Elasticsearch、MongoDb、Redis等也可以用来存储向量。
下面我们使用比较简单的DuckDB来实现文本话存储以及相似度匹配查询
1.8.2 DuckDB简介
DuckDB 是什么?
DuckDB 是一个开源的 嵌入式分析型数据库(OLAP),专为高效的数据分析场景设计。它类似于 SQLite,但专注于高性能的联机分析处理(OLAP),而非事务处理(OLTP)。其核心特点包括:
- 嵌入式设计:无需独立服务器,直接嵌入到应用程序中运行。
- 轻量级:单文件存储,依赖极少,部署简单。
- 高性能:针对复杂分析查询优化,支持列式存储和向量化执行引擎。
- 兼容性:支持标准 SQL 语法,提供 Python、R、Java、C/C++ 等语言接口。
适用场景
- 数据分析与探索:快速处理本地 CSV/Parquet 文件,替代 Pandas 或 Excel 的大数据场景。
- 嵌入式分析:在应用程序中内置高性能分析功能(如报表生成、复杂统计)。
- 数据科学:与 Python/R 集成,替代临时性的 SQLite 或内存计算。
- 边缘计算:在资源受限的设备上运行轻量级分析任务。
存储方式
- 单文件存储
与其他数据库对比
特性 | DuckDB | SQLite | PostgreSQL |
---|---|---|---|
设计目标 | OLAP | OLTP | OLTP/OLAP |
部署模式 | 嵌入式 | 嵌入式 | 独立服务 |
存储格式 | 单文件 | 单文件 | 多文件目录 |
分析性能 | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐ |
事务支持 | 有限 | ACID | ACID |
集成使用
pom.xml依赖引入:
<!-- simple-duckdb--><dependency><groupId>org.duckdb</groupId><artifactId>duckdb_jdbc</artifactId><version>1.2.1</version></dependency><!--langchain4j-duckdb--><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-community-duckdb</artifactId><version>${langchain4j.version}</version></dependency>
简单的测试:
1-简单的连接使用
/*** @version 1.0* @Author jerryLau* @Date 2025/3/31 10:00* @注释 DuckDB 连接测试*/
public class DuckDBConnectExample {public static void main(String[] args) {String url="jdbc:duckdb:/duck.db";try(Connection connection = DriverManager.getConnection(url)){if (connection.isValid(1000)) {System.out.println("DuckDB 连接成功");}} catch (SQLException e) {throw new RuntimeException(e);}}
}
- jdbc:duckdb:langchain4j-springBoot-demo/src/main/java/com/jerry/langchain4jspringbootdemo/store/db/duck.db 默认会在绝对路径下创建这个db文件作为数据库
2-使用CRUD
/*** @version 1.0* @Author jerryLau* @Date 2025/3/31 10:00* @注释 DuckDB 操作测试*/
public class DuckDBExample {public static void main(String[] args) throws SQLException {queryTable();}public static void createTable() throws SQLException {String url = "jdbc:duckdb:langchain4j-springBoot-demo/src/main/java/com/jerry/langchain4jspringbootdemo/store/db/duck.db";try (Connection connection = DriverManager.getConnection(url)) {if (connection.isValid(1000)) {System.out.println("DuckDB 连接成功");connection.createStatement().execute("CREATE TABLE IF NOT EXISTS test (id INTEGER, name VARCHAR)");System.out.println("创建表成功");int rowsInserted = connection.createStatement().executeUpdate("INSERT INTO test VALUES (1, 'Jerry')");System.out.println("插入数据行数: " + rowsInserted);if (rowsInserted > 0) {System.out.println("插入数据成功");} else {System.out.println("插入数据失败");}}} catch (SQLException e) {throw new RuntimeException(e);}}public static void queryTable() throws SQLException {String url = "jdbc:duckdb:langchain4j-springBoot-demo/src/main/java/com/jerry/langchain4jspringbootdemo/store/db/duck.db";try (Connection connection = DriverManager.getConnection(url)) {if (connection.isValid(1000)) {System.out.println("DuckDB 连接成功");ResultSet rs = connection.createStatement().executeQuery("SELECT * FROM test");while (rs.next()) {System.out.println(rs.getString("id") + "\t" + rs.getString("name")+ "\t" );}}} catch (SQLException e) {throw new RuntimeException(e);}}
}
查询时可以看到这个内容已经存储进去了,同时也可以使用DG进行连接查看数据库数据,但注意一点:代码或者可视化工具两个不能同时查询,否则会报错文件正在被另一个线程使用中。
1.8.3 匹配向量
我们考虑做这样一件事情:
- 构造一个十分简单的人工客服,为用户提供预定以及取消预订服务
- 向量存储预定以及取消预订的相关条款
- 根据用户传入的提示词,进行匹配度筛选,返回给用户匹配的描述
在整个过程中流程类似于下图所示:
那么我们一起实现:
package com.jerry.langchain4jspringbootdemo.example.embedding;import dev.langchain4j.community.model.dashscope.QwenEmbeddingModel;
import dev.langchain4j.community.store.embedding.duckdb.DuckDBEmbeddingStore;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.store.embedding.EmbeddingMatch;
import dev.langchain4j.store.embedding.EmbeddingSearchRequest;/*** @version 1.0* @Author jerryLau* @Date 2025/3/31 8:35* @注释 向量化示例*/
public class EmBeddingExample2 {public static void main(String[] args) {DuckDBEmbeddingStore store = DuckDBEmbeddingStore.builder().filePath("langchain4j-springBoot-demo/src/main/java/com/jerry/langchain4jspringbootdemo/store/db/aiAssitant.db").tableName("message").build();QwenEmbeddingModel embeddingModel = QwenEmbeddingModel.builder().apiKey("sk-xxxxxxxx")
// .modelName("text-embedding-v3")
// .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1").build();TextSegment segment = TextSegment.from("""线上预约:1. 选择预约时间段:在预约页面,您可以选择适合您的时间段进行预约。2. 选择服务项目:根据您的需求,选择适合您的服务项目。3. 填写预约信息:在预约页面,您需要填写您的姓名、联系方式、预约日期和预约时间等信息。4. 提交预约:在填写完预约信息后,您需要点击提交按钮,提交您的预约信息。5. 支付预约:在提交预约后,您需要支付预约费用15元人民币。6. 预约成功:在支付预约成功后,您的预约将被成功提交。""");TextSegment segment2 = TextSegment.from("""线上解除预约:1. 选择预约记录:在预约记录页面,您可以选择需要解除预约的记录。2. 确认取消:在确认取消页面,您需要确认是否取消预约,最晚取消时间在活动开始时间前12小时。3. 在活动开始时间前12小时取消不收取手续费,否则按照预约费用的12%收取手续费。4. 取消成功:在确认取消后,您的预约将被成功取消,费用将于7个工作日内原路返回至您的支付账户。""");Embedding content = embeddingModel.embed(segment).content();Embedding content2 = embeddingModel.embed(segment2).content();store.add(content, segment);store.add(content2, segment2);var queryEmbedding = embeddingModel.embed("取消预约").content();var request = EmbeddingSearchRequest.builder().queryEmbedding(queryEmbedding).maxResults(1).build();var relevant = store.search(request);EmbeddingMatch<TextSegment> embeddingMatch = relevant.matches().get(0);// Show resultsSystem.out.println(embeddingMatch.score());//文本相似度System.out.println(embeddingMatch.embedded().text()); //相似文本输出}
}
输出结果为
那么到这一步,外接知识库的操作已经快完成了,只需要将获取到的“知识”在此投喂给ai,使得ai能够极大程度的更具你的“知识”提供回答即可,接着往下看。
2.RAG知识库集成
RAG知识库只需要在上述流程后中在添加一些流程即可,具体图解如下
知识库的集成我们 大概要干这么几件事情:
- 获取一个pdf文件,做为我们外接的“知识库”,此处以一个java知识点pdf文件为例子
- 完成pdf文件读取
- 完成pdf文件内容拆分
- 拆分内容文本向量化
- 获取pdf拆分后的文本
- 进行文本向量化
- 向量化文本存储至向量数据库,此处我们以存储至DuckDB为例子
- 搭建javaAssistant,将存储的向量知识配置到ai模型上,进行询问java相关知识,查看输出结果
接下来 开干
2.1 文档读取与文档解析
优先集成langchain4j的文档读取, langchain4j提供了比较多的文档解析器,例如解析txt,excle,pdf等,需要的小伙伴官网自取
langchain4j文档读取|Document Loaders
langchain4j文档解析|Document Parsers
此处选择集成 Apache PDFBox 实现pdf文件的读取与解析
//pdf文档读取与解析
InputStream inputStream = RAGExample.class.getClassLoader().getResourceAsStream("java.pdf");DocumentParser parser = new ApachePdfBoxDocumentParser();Document document = parser.parse(inputStream);System.out.println(document.text());
2.2文档拆分
文档拆分器的作用
在基于大语言模型(LLM)的应用中,文档拆分器用于将长文本分割成较小的片段(chunks),以解决以下问题:
- 突破 LLM 的输入长度限制(如 GPT-3 最大 4096 tokens)。
- 提高处理效率:分块后可以并行处理或逐步分析。
- 保留上下文:通过重叠分块(overlap)避免信息割裂。
langchain4j 支持的拆分器类型
langchain4j 的文档拆分器位于包 dev.langchain4j.data.document.splitter
中,主要分为以下类型:
分词器类型 | 匹配能力 | 适用场景 |
---|---|---|
DocumentByCharacterSplitter | 无符号分割 | 就是严格根据字数分隔(不推荐,会出现断句) |
DocumentByRegexSplitter | 正则表达式分隔 | 根据自定义正则分隔 |
DocumentByParagraphSplitter | 删除大段空白内容 | 处理连续换行符(如段落分隔)(\s*(?>\R)\s*(?>\R)\s* |
DocumentByLineSplitter | 删除单个换行符周围的空白, 替换一个换行 | (\s*\R\s*) ●示例: ○输入文本:“This is line one.\n\tThis is line two.” ○使用 \s*\R\s* 替换为单个换行符:“This is line one.\nThis is line two.” |
DocumentByWordSplitter | 删除连续的空白字符。 | \s+ ●示例: ○输入文本:“Hello World” ○使用 \s+ 替换为单个空格:“Hello World” |
DocumentBySentenceSplitter | 按句子分割 | Apache OpenNLP 库中的一个类,用于检测文本中的句子边界。它能够识别标点符号(如句号、问号、感叹号等)是否标记着句子的末尾,从而将一个较长的文本字符串分割成多个句子。 |
因为我自己尝试过好几种拆分,比如段落拆分,行拆分,句子拆分,对于java.pdf来说不同的问题,存在不一样长度的答案,不太能把握住拆分的长度,拆分长度过长或者过短都会影响拆分结果,出现文本粘连的情况。
在我观察这个文本之后,便选择出了使用正则表达式的方式拆分,正则表达判定序号+"、"作为拆分片段开始,个人感觉拆分的结果还不错。
// 构造正则分割器(演示增强版正则)DocumentByRegexSplitter splitter = new DocumentByRegexSplitter("(\\n\\d+、)", // 核心匹配模式"\n", // 拼接保留换行220, // 根据平均题目+答案长度调整20 // 小幅度重叠避免切断句子);// 执行分割String[] rawSegments = splitter.split(preprocessedText);// 合并过小片段(如独立存在的答案行)
// List<TextSegment> merged = mergeSmallSegments(rawSegments, 100);List<TextSegment> merged = new ArrayList<>();for (int i = 0; i < rawSegments.length; i++) {merged.add(TextSegment.from(rawSegments[i]));}
2.3文本向量化
将拆分出来的小片段进行向量化,并存入DuckDB
QwenEmbeddingModel embeddingModel = QwenEmbeddingModel.builder().apiKey("sk-xxxxxxx").build();List<Embedding> content = embeddingModel.embedAll(merged).content();DuckDBEmbeddingStore store = DuckDBEmbeddingStore.builder().filePath("langchain4j-springBoot-demo/src/main/java/com/jerry/langchain4jspringbootdemo/store/db/javaInfo.db").tableName("message").build();store.addAll(content, merged);
简单存储到DuckDB后数据大致是这个样子
2.4集成简单的问答
简单的输入问答,返回匹配到的相似度较高的答案
var queryEmbedding = embeddingModel.embed("帮我理解一下java 的 代理模式").content();var request = EmbeddingSearchRequest.builder().queryEmbedding(queryEmbedding)
// .maxResults(1).minScore(0.8).build();var relevant = store.search(request);List<EmbeddingMatch<TextSegment>> matches = relevant.matches();matches.forEach(embeddingMatch -> {System.out.println(embeddingMatch.score());System.out.println(embeddingMatch.embedded().text());});
2.5集体集成值SpringBoot项目中
再次配置一个新的javaAssistant,引入向量数据库,向量查询,为了简单演示 记忆暂时使用内存保存
package com.jerry.langchain4jspringbootdemo.config;import com.jerry.langchain4jspringbootdemo.service.ToolService;
import dev.langchain4j.community.model.dashscope.QwenEmbeddingModel;
import dev.langchain4j.community.store.embedding.duckdb.DuckDBEmbeddingStore;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.chat.StreamingChatLanguageModel;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.TokenStream;
import dev.langchain4j.store.embedding.EmbeddingStore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** @version 1.0* @Author jerryLau* @Date 2025/3/26 13:33* @注释 ai配置类 java 助手*/
@Configuration
public class AIConfJava {/**** java助手接口*/public interface ChatAssistantJava {/**** 普通聊天 非隔离上下文* @param question* @return*/String chat(String question);/**** 流式输出 非隔离上下文* @param question* @return*/TokenStream streamChat(String question);}@Beanpublic EmbeddingStore embeddingStore() {DuckDBEmbeddingStore store = DuckDBEmbeddingStore.builder().filePath("langchain4j-springBoot-demo/src/main/java/com/jerry/langchain4jspringbootdemo/store/db/javaInfo.db").tableName("message").build();return store;}@Beanpublic QwenEmbeddingModel qwenEmbeddingModel() {QwenEmbeddingModel embeddingModel = QwenEmbeddingModel.builder().apiKey("sk-xxxxxxx").build();return embeddingModel;}/**** 注入聊天服务 非隔离上下文* @param chatLanguageModel* @param streamingChatLanguageModel* @return*/@Beanpublic ChatAssistantJava chatAssistantJava(ChatLanguageModel chatLanguageModel,StreamingChatLanguageModel streamingChatLanguageModel,EmbeddingStore embeddingStore,QwenEmbeddingModel qwenEmbeddingModel,ToolService toolService) {//使用简单的ChatMemory - MessageWindowChatMemory来保存聊天记录MessageWindowChatMemory messageWindowChatMemory = MessageWindowChatMemory.builder().maxMessages(10).build(); //最多保存10条记录//设置向量库EmbeddingStoreContentRetriever retriever = EmbeddingStoreContentRetriever.builder().embeddingStore(embeddingStore) //向量库 duckdb.embeddingModel(qwenEmbeddingModel)//向量化模型.minScore(0.8) //匹配度80%以上.build();ChatAssistantJava build = AiServices.builder(ChatAssistantJava.class).chatLanguageModel(chatLanguageModel).streamingChatLanguageModel(streamingChatLanguageModel).chatMemory(messageWindowChatMemory)
// .tools(toolService).contentRetriever(retriever).build();return build;}}
简单创建新的Controller进行接口请求
package com.jerry.langchain4jspringbootdemo.controller;import com.jerry.langchain4jspringbootdemo.config.AIConf;
import com.jerry.langchain4jspringbootdemo.config.AIConfJava;
import com.jerry.langchain4jspringbootdemo.service.AiService;
import dev.langchain4j.community.model.dashscope.QwenChatModel;
import dev.langchain4j.model.chat.response.ChatResponse;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;/*** @version 1.0* @Author jerryLau* @Date 2025/3/20 10:54* rag 例子 接口* @注释*/@RestController
@RequestMapping("/ai/rag")
public class LangChain4jRagController {@Resourceprivate AIConfJava.ChatAssistantJava chatAssistantJava;@GetMapping("/doAsk")public String langChain4j(@RequestParam(defaultValue = "帮我理解一下 代理模式") String question) {return chatAssistantJava.chat(question);}
}
分别使用先前的普通调用及RAG知识库调用接口,询问“ArrayList 和 Array 的区别”这一问题
首先展示一下java.pdf中对于这一问题是怎么描述的
对比下图,左边普通的调用时没有参考任何知识点,只是将这个问题解答了出来,右边通过RAG知识库,AI的解答中大致上是通过上面知识库中的大纲进行的描述解答,可以清楚的看到"ArrayList是一个泛型类"等字眼。
正常普通的调用返回
`ArrayList` 和 `Array` 都是用于存储数据集合的数据结构,但它们在实现方式、灵活性以及性能上存在一些关键差异。下面是两者之间的一些主要区别:1. **类型定义**:- `Array`(数组)是一种基本的数据结构,它允许你存储固定大小的同类型元素。- `ArrayList` 是 Java 中的一个类,实现了 List 接口,可以动态地调整其大小,并且能够存储不同类型的对象(通过使用泛型)。2. **大小调整**:- 对于 `Array`,一旦创建后,它的大小是固定的。如果需要改变数组的大小,必须创建一个新的数组并将旧数组中的元素复制到新数组中。- `ArrayList` 提供了自动增长的能力,当添加新的元素超过当前容量时,它会自动增加其内部数组的大小。3. **内存分配**:- 数组直接在栈或堆上分配连续的内存空间来存储所有元素。- `ArrayList` 内部也使用一个数组来存储元素,但它是在堆上分配的,并且这个内部数组可以根据需要重新分配和扩展。4. **访问速度**:- 由于数组提供的是基于索引的快速随机访问,所以访问特定位置上的元素非常快。- `ArrayList` 同样支持基于索引的快速访问,因为底层实现也是基于数组的。5. **插入与删除操作**:- 在数组中插入或删除元素通常比较低效,特别是对于大型数组,因为这可能涉及到移动大量元素以保持其他元素的位置不变。- `ArrayList` 虽然提供了方便的方法来进行插入和删除操作,但在列表中间进行这些操作仍然可能导致效率问题,因为它也需要移动元素。不过,在列表末尾添加或移除元素相对更高效。6. **功能性和便利性**:- 数组提供了最基本的功能,如访问、遍历等。- `ArrayList` 除了基础功能外,还提供了更多高级功能,比如排序、搜索、迭代器支持等,使得处理集合更加方便。7. **多线程安全性**:- 标准的数组不是线程安全的。- 基础的 `ArrayList` 实现也不是线程安全的。如果需要在多线程环境中使用,可以考虑使用 `Collections.synchronizedList()` 方法或者选择 `CopyOnWriteArrayList` 类。总之,选择使用 `ArrayList` 还是 `Array` 取决于具体的应用场景和需求。如果你需要一个固定大小的简单集合并且关注性能,那么数组可能是更好的选择;而如果你希望拥有更多的灵活性以及额外的功能,则 `ArrayList` 会更适合。
外界知识库后的调用返回:
根据提供的信息,以下是ArrayList和Array的主要区别:### ArrayList 和 Array 的区别1. **类型差异**:- **ArrayList** 是一个泛型类,属于Java集合框架的一部分。它使用泛型来存储对象,并且对于基本数据类型(如 `int`, `char` 等),需要将其转换为对应的包装类(例如 `Integer`, `Character`)才能存储。- **Array** 是Java语言的基本数据结构之一。它可以直接存储基本数据类型或对象引用。2. **动态性与固定性**:- **ArrayList** 是一个动态数组,可以根据需要自动调整其容量大小以容纳更多的元素,即支持自动扩容。- **Array** 一旦创建后,其大小是固定的,不能再改变。如果需要改变数组的大小,必须创建一个新的数组并将旧数组中的元素复制到新数组中。3. **可变长度 vs 固定长度**:- **ArrayList** 是一个可变长度的数组,可以随时添加或删除元素。- **Array** 是一个固定长度的数组,不能在创建后改变其大小。4. **操作方法的支持**:- **ArrayList** 提供了丰富的内置方法来进行动态添加、删除和访问元素,如 `add()`, `remove()`, `get()`, `set()` 等。- **Array** 不提供这些内置方法。对数组的操作通常需要通过下标遍历进行,例如手动编写代码来实现添加和删除元素。### 总结- **类型**:ArrayList是泛型类,需要将基本数据类型转换为包装类;Array可以直接存储基本数据类型。 - **动态性**:ArrayList是动态数组,支持自动扩容;Array是静态数组,大小固定。 - **长度**:ArrayList是可变长度的数组;Array是固定长度的数组。 - **操作方法**:ArrayList提供了丰富的内置方法进行动态操作;Array没有这些内置方法,需要手动处理。希望这些信息能帮助你更好地理解ArrayList和Array之间的区别。如果你还有其他问题,请随时告诉我!
那么截至到目前位置,RAG知识库的集成完成:✿✿ヽ(°▽°)ノ✿
码字不易,各位观众老爷如果喜欢,别忘了一键三连,支持一波,如果描述中有不当的地方,还请各位大佬指正
代码仓库已上传 github
相关文章:
Java开发者の模型召唤术:LangChain4j咏唱指南(三)
Java开发者の模型召唤术:LangChain4j咏唱指南(三) 往期回顾: Java开发者の模型召唤术:LangChain4j咏唱指南(一)Java开发者の模型召唤术:LangChain4j咏唱指南(二) 上两期博客中简单的为大家介绍了 langchain4j是什么、java 集成…...
【leetcode100】动态规划Java版本
70. 爬楼梯 题目 思考的时候觉得情况很多,无从下手,卡在了找推导公式这一步。 看了随想录后知道以简单的三个阶梯来推导dp公式,为什么不是四个,五个为一组呢?因为题目要求的只能爬1个阶梯,或者2个阶梯&…...
RSA和ECC在密钥长度相同的情况下哪个更安全?
现在常见的SSL证书,如:iTrustSSL都支持RSA和ECC的加密算法,正常情况下RAS和ECC算法该如何选择呢?实际上在密钥长度相同的情况下,ECC(椭圆曲线密码学)通常比RSA(Rivest-Shamir-Adle…...
YOLO 获取 COCO 指标终极指南 | 从标签转换到 COCOAPI 评估 (训练/验证) 全覆盖【B 站教程详解】
✅ YOLO 轻松获取论文 COCO 指标:AP(small,medium,large )| 从标签转换到 COCOAPI 评估 (训练/验证) 全覆盖 文章目录 一、摘要二、为什么需要 COCO 指标评估 YOLO 模型?三、核心挑战与解决方案 (视频教程核…...
【算法竞赛】dfs+csp综合应用(蓝桥2023A9像素放置)
目录 一、 题目 二、思路 (1)算法框架选择 (2)剪枝策略 具体来说就是: 三、代码 (1) 数据读取与初始化 (2) 检查当前填充是否符合要求 (3) 递归 DFS 进行填充 (4) 读取输入 & 调用 DFS (5) 完整代码 一…...
3D点云配准RPM-Net模型解读(附论文+源码)
RPM-Net 总体流程代码数据预处理模型计算 α α α和 β β β特征提取变换矩阵计算损失 论文链接:RPM-Net: Robust Point Matching using Learned Features 官方链接:RPMNet 老规矩,先看看效果。 看看论文里给的对比图 总体流程 在学…...
23种设计模式-行为型模式-命令
文章目录 简介问题解决代码核心设计优势 总结 简介 命令是一种行为设计模式, 它能把请求转换为一个包含与请求相关的所有信息 的独立对象。这个转换能让你把请求方法参数化、延迟请求执行或把请求放在队列里,并且能实现可撤销操作。 问题 假如你正在开…...
ngx_cpystrn
定义在 src\core\ngx_string.c u_char * ngx_cpystrn(u_char *dst, u_char *src, size_t n) {if (n 0) {return dst;}while (--n) {*dst *src;if (*dst \0) {return dst;}dst;src;}*dst \0;return dst; } ngx_cpystrn 函数的作用是安全地将源字符串(src&#x…...
常用的国内镜像源
常见的 pip 镜像源 阿里云镜像:https://mirrors.aliyun.com/pypi/simple/ 清华大学镜像:https://pypi.tuna.tsinghua.edu.cn/simple 中国科学技术大学镜像:https://pypi.mirrors.ustc.edu.cn/simple/ 豆瓣镜像:https://pypi.doub…...
【小沐杂货铺】基于Three.JS绘制太阳系Solar System(GIS 、WebGL、vue、react)
🍺三维数字地球系列相关文章如下🍺:1【小沐学GIS】基于C绘制三维数字地球Earth(456:OpenGL、glfw、glut)第一期2【小沐学GIS】基于C绘制三维数字地球Earth(456:OpenGL、glfw、glut)第二期3【小沐…...
Navicat17详细安装教程(附最新版本安装包和补丁)2025最详细图文教程安装手册
目录 前言:为什么选择Navicat 17? 一、下载Navicat17安装包 二、安装Navicat 1.运行安装程序 2.启动安装 3.同意“协议” 4.设置安装位置 5.创建桌面图标 6.开始安装 7.安装完成 三、安装补丁 1.解押补丁包 2.在解压后的补丁包目录下找到“w…...
记忆宫殿APP:全方位脑力与思维训练,助你提升记忆力,预防老年痴呆
记忆宫殿APP,一款专业的记忆训练软件,能去帮你提升自己的记忆能力,多样的训练项目创新的记忆方法,全方面帮你去提升你的记忆能力。 记忆宫殿APP有丰富的记忆训练项目,如瞬间记忆、短时记忆、机械记忆等,以…...
SpringBoot+Spring+MyBatis相关知识点
目录 一、相关概念 1.spring框架 2.springcloud 3.SpringBoot项目 4.注解 5.SpringBoot的文件结构 6.启动类原理 二、相关操作 1.Jar方式打包 2.自定义返回的业务状态码 3.Jackson 4.加载配置文件 5.异常处理 三、优化配置 1.简化sql语句 2.查询操作 复杂查询 一…...
【力扣hot100题】(050)岛屿数量
一开始还以为会很难很难(以为暴力搜索会时间超限要用别的办法),没想到并不难。 我最开始是用vector<vector<bool>>记录搜索过的地域,每次递归遍历周围所有地域。 class Solution { public:vector<vector<char…...
Opencv计算机视觉编程攻略-第九节 描述和匹配兴趣点
一般而言,如果一个物体在一幅图像中被检测到关键点,那么同一个物体在其他图像中也会检测到同一个关键点。图像匹配是关键点的常用功能之一,它的作用包括关联同一场景的两幅图像、检测图像中事物的发生地点等等。 1.局部模板匹配 凭单个像素就…...
pat学习笔记
two pointers 双指针 给定一个递增的正整数序列和一个正整数M,求序列中的两个不同位置的数a和b,使得它们的和恰好为M,输出所有满足条件的方案。例如给定序列{1,2,3,4,5,6}和正整数M 8,就存在268和358成立。 容易想到࿱…...
MoE Align Sort在医院AI医疗领域的前景分析(代码版)
MoE Align & Sort技术通过优化混合专家模型(MoE)的路由与计算流程,在医疗数据处理、模型推理效率及多模态任务协同中展现出显著优势,其技术价值与应用意义从以下三方面展开分析: 一、方向分析 1、提升医疗数据处理效率 在医疗场景中,多模态数据(如医学影像、文本…...
大数据概念介绍
这节课给大家讲一下大数据的生态架构, 大数据有很多的产品琳琅满目, 大家看到图上就知道产品很多, 那这些产品它们各自的功能是什么, 它们又是怎么样相互配合, 来完成一整套的数据存储, 包括分析计算这样的一些任务, 这节课就要给大家进行一个分析, 那我们按照数据处理…...
Linux(2025.3.15)
1、将/etc/passwd,/etc/shadow,/etc/group文件复制到/ceshi; 操作: (1)在根目录下创建ceshi目录; (2)复制; 结果: 2、找到/etc/ssh目录下ssh开头的所有文件并将其复制到…...
centosububntu设置开机自启动
一、centos 1.将脚本放到/etc/rc.d/init.d路径下 2.给脚本授权 sudo chmod -R 777 脚本名称 3.添加脚本至开机启动项中 sudo chkconfig --add 脚本名称 4.开启脚本 sudo chkconfig 脚本名称 on 5.查看开机启动项中是否包含该脚本 ls /etc/rc.d/rc*.d 二、ubuntu 1.在…...
基于大模型与动态接口调用的智能系统(知识库实现)
目录 引言 1、需求背景 2、实现原理 3、实现步骤 3.1 构建知识库接口调用提示模板 3.2 动态接口配置加载 3.3 智能参数提取链 3.4 接口智能路由 3.5 建议生成链 3.6 组合完整工作流 3.7 展示效果 总结 引言 在医疗信息化快速发展的今天,我们开发了一个智能问诊系…...
23种设计模式-行为型模式-迭代器
文章目录 简介问题解决代码设计关键点: 总结 简介 迭代器是一种行为设计模式,让你能在不暴露集合底层表现形式(列表、栈和树等)的情况下遍历集合中所有的元素。 问题 集合是编程中最常使用的数据类型之一。 大部分集合使用简单列表存储元素。但有些集…...
【Java集合】ArrayList源码深度分析
参考笔记: java ArrayList源码分析(深度讲解)-CSDN博客 【源码篇】ArrayList源码解析-CSDN博客 目录 1.前言 2. ArrayList简介 3. ArrayList类的底层实现 4. ArrayList的源码Debug 4.1 使用空参构造 (1)开始De…...
ISIS单区域抓包分析
一、通用头部报文 Intra Domain Routing Protocol Discriminator:域内路由选择协议鉴别符:这里是ISIS System ID Length:NSAP地址或NET中System ID区域的长度。值为0时,表示System ID区域的长度为6字节。值为255时,表…...
关键业务数据如何保持一致?主数据管理的最佳实践!
随着业务规模的扩大和系统复杂性的增加,如何确保关键业务数据的一致性成为许多企业面临的重大挑战。数据不一致可能导致决策失误、运营效率低下,甚至影响客户体验。因此,主数据管理(Master Data Management,简称MDM&am…...
ISIS单区域配置
一、什么是ISIS单区域 ISIS(Intermediate System to Intermediate System,中间系统到中间系统)单区域是指使用ISIS路由协议时,所有路由器都位于同一个区域(Area)内的网络配置。 二、实验拓扑 三、实验目的…...
Visual Basic语言的物联网
Visual Basic语言在物联网中的应用 引言 物联网(IoT)作为一种新兴的技术趋势,正在深刻地改变我们的生活方式与工业制造过程。在众多编程语言中,Visual Basic(VB)凭借其简单易用的特性,逐渐成为…...
【小沐杂货铺】基于Three.JS绘制三维数字地球Earth(GIS 、WebGL、vue、react)
🍺三维数字地球系列相关文章如下🍺:1【小沐学GIS】基于C绘制三维数字地球Earth(456:OpenGL、glfw、glut)第一期2【小沐学GIS】基于C绘制三维数字地球Earth(456:OpenGL、glfw、glut)第二期3【小沐…...
Vite环境下解决跨域问题
在 Vite 开发环境中,可以通过配置代理来解决跨域问题。以下是具体步骤: 在项目根目录下找到 vite.config.js 文件:如果没有,则需要创建一个。配置代理:在 vite.config.js 文件中,使用 server.proxy 选项来…...
嵌入式Linux开发环境搭建,三种方式:虚拟机、物理机、WSL
目录 总结写前面一、Linux虚拟机1 安装VMware、ubuntu18.042 换源3 改中文4 中文输入法5 永不息屏6 设置 root 密码7 安装 terminator8 安装 htop(升级版top)9 安装 Vim10 静态IP-虚拟机ubuntu11 安装 ssh12 安装 MobaXterm (SSH)…...
React项目在ts文件中使用router实现跳转
前言: 默认你已经进行了router的安装,目前到了配置http请求的步骤,在配置token失效或其他原因,需要实现路由跳转。在普通的 TypeScript 文件中无法直接使用Router的 useNavigate Hook。Hook 只能在 React 组件或自定义 Hook 中调用…...
Java中的正则表达式Lambda表达式
正则表达式&&Lambda表达式 正则表达式和Lambda表达式是Java编程中两个非常实用的特性。正则表达式用于字符串匹配与处理,而Lambda表达式则让函数式编程在Java中变得更加简洁。本文将介绍它们的基本用法,并结合示例代码帮助理解。同时要注意&…...
【idea设置文件头模板】
概述 设置模板,在创建java类时,统一添加内容(作者、描述、创建时间等等自定义内容),给java类添加格式统一的备注信息。 1、在settings 中找到File and Code Templates 选择File Header 2、模板内容示例 /*** Author hweiyu* Descriptio…...
我与数学建模之顺遂!
下面一段时期是我一段真正走进数模竞赛的时期。 在大二上学期结束之后,就开始张罗队友一起报名参加美赛,然后同时开始学LaTeX和Matlab,当时就是买了本Matlab的书,把书上的例题还有课后题全部做完了,然后用latex将书上…...
【Python使用】嘿马推荐系统全知识和项目开发教程第2篇:1.4 案例--基于协同过滤的电影推荐,1.5 推荐系统评估【附代码
教程总体简介:1.1 推荐系统简介 学习目标 1 推荐系统概念及产生背景 2 推荐系统的工作原理及作用 3 推荐系统和Web项目的区别 1.3 推荐算法 1 推荐模型构建流程 2 最经典的推荐算法:协同过滤推荐算法(Collaborative Filtering) 3 …...
Linux makefile的一些语法
一、定义变量 1. 变量的基本语法 在 makefile 中,变量的定义和使用非常类似于编程语言中的变量。变量的定义格式(最好不要写空格)如下: VARIABLE_NAMEvalue 或者 VARIABLE_NAME:value 表示延迟赋值,变量的值在引…...
Educational Codeforces Round 177 (Rated for Div. 2)(A-D)
题目链接:Dashboard - Educational Codeforces Round 177 (Rated for Div. 2) - Codeforces A. Cloudberry Jam 思路 小数学推导问题,直接输出n*2即可 代码 void solve(){int n;cin>>n;cout<<n*2<<"\n"; } B. Large A…...
第十八节课:Python编程基础复习
课程复习 前三周核心内容回顾 第一周:Python基本语法元素 基础语法:缩进、注释、变量命名、保留字数据类型:字符串、整数、浮点数、列表程序结构:赋值语句、分支语句(if)、函数输入输出:inpu…...
动物多导生理信号采集分析系统技术简析
一 技术参数 通道数:通道数量决定了系统能够同时采集的生理信号数量。如中南大学湘雅医学院的生物信号采集系统可达 128 通道,OmniPlex 多导神经信号采集分析系统支持 16、32、64、128 通道在体记录。不过,这个也要看具体的应用场景ÿ…...
Linux——Linux系统调用函数练习
一、实验名称 Linux系统调用函数练习 二、实验环境 阿里云服务器树莓派 三、实验内容 1. 远程登录阿里云服务器 2. 创建目录 操作步骤: mkdir ~/xmtest2 cd ~/xmtest2结果: 成功创建并进入homework目录。 3. 编写C代码 操作步骤: …...
列表与列表项
认识列表和列表项 FreeRTOS 中的 列表(List) 和 列表项(ListItem)是其内核实现的核心数据结构,广泛用于任务调度、队列管理、事件组、信号量等模块。它们通过双向链表实现,支持高效的元素插入、删除和遍历…...
mofish软件(MacOS版本)手动初始化
mofish软件手动初始化MacOS 第一步,打开终端 command空格键唤起搜索页面,输入终端,点击打开终端 第二步,进入mofish配置目录,删除初始化配置文件 在第一步打开的终端中输入如下命令后按回车键,删除mofish配置文件 …...
基于javaweb的SpringBoot图片管理系统图片相册系统设计与实现(源码+文档+部署讲解)
技术范围:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论文…...
密码学基础——DES算法
前面的密码学基础——密码学文章中介绍了密码学相关的概念,其中简要地对称密码体制(也叫单钥密码体制、秘密密钥体制)进行了解释,我们可以知道单钥体制的加密密钥和解密密钥相同,单钥密码分为流密码和分组密码。 流密码࿰…...
我与数学建模之波折
我知道人生是起起伏伏,但没想到是起起伏伏伏伏伏伏 因为简单讲讲,所以我没讲很多生活上的细节,其实在7月我和l学长一起在外面租房子备赛。这个时间节点其实我不太愿意讲,但是逃不了,那段时间因其他事情导致我那段时间…...
离线部署kubesphere(已有k8s和私有harbor的基础上)
前言说明:本文是在已有k8s集群和私有仓库harbor上进行离线安装kubesphere;官网的离线教程写都很详细,但是在部署部份把搭建集群和搭建仓库也写一起了,跟着做踩了点坑,这里就记录下来希望可以帮助到需要的xdm。 1.根据官…...
量子计算入门:Qiskit实战量子门电路设计
引言:量子计算的编程基石 量子门是量子计算的基本操作单元,其通过操控量子比特的叠加与纠缠实现并行计算。IBM开发的Qiskit框架为量子算法设计与模拟提供了强大工具。本文将从量子门基础、Qiskit实战、量子隐形传态案例三个维度,结合代码解析…...
AIGC8——大模型生态与开源协作:技术竞逐与普惠化浪潮
引言:大模型发展的分水岭时刻 2024年成为AI大模型发展的关键转折点:OpenAI的GPT-4o实现多模态实时交互,中国DeepSeek-MoE-16b模型以1/8成本达到同类90%性能,而开源社区如Mistral、LLama 3持续降低技术门槛。这场"闭源商业巨…...
FPGA练习
文章目录 一、状态机思想写一个 LED流水灯的FPGA代码二、 CPLD和FPGA芯片的主要技术区别是什么? 它们各适用于什么场合?1、CPLD适用场景2、FPGA适用场景 三、 在hdlbitsFPGA教程网站上进行学习1、练习题12、练习题2练习题3练习题4练习题5 一、状态机思想…...
阿里云服务器遭遇DDoS攻击有争议?
近年来,阿里云服务器频繁遭遇DDoS攻击的事件引发广泛争议。一方面,用户质疑其防御能力不足,导致服务中断甚至被迫进入“黑洞”(清洗攻击流量的隔离机制),轻则中断半小时,重则长达24小时…...