当前位置: 首页 > news >正文

MMKV(1)

  • 内存准备

通过 mmap 内存映射文件,提供一段可供随时写入的内存块,App 只管往里面写数据,由操作系统负责将内存回写到文件,不必担心 crash 导致数据丢失。

  • 数据组织

数据序列化方面选用 protobuf 协议,pb 在性能和空间占用上都有不错的表现。考虑到要提供的是通用 kv 组件,key 可以限定是 string 字符串类型,value 则多种多样(int/bool/double 等)。要做到通用的话,考虑将 value 通过 protobuf 协议序列化成统一的内存块(buffer),然后就可以将这些 KV 对象序列化到内存中。

message KV {string key = 1;buffer value = 2;
}-(BOOL)setInt32:(int32_t)value forKey:(NSString*)key {auto data = PBEncode(value);return [self setData:data forKey:key];
}-(BOOL)setData:(NSData*)data forKey:(NSString*)key {auto kv = KV { key, data };auto buf = PBEncode(kv);return [self write:buf];
}
  • 写入优化

考虑到主要使用场景是频繁地进行写入更新,需要有增量更新的能力。考虑将增量 kv 对象序列化后,append 到内存末尾。标准 protobuf 不提供增量更新的能力,每次写入都必须全量写入。考虑到主要使用场景是频繁地进行写入更新,需要有增量更新的能力:将增量 kv 对象序列化后,直接 append 到内存末尾;这样同一个 key 会有新旧若干份数据,最新的数据在最后;那么只需在程序启动第一次打开 mmkv 时,不断用后读入的 value 替换之前的值,就可以保证数据是最新有效的。

  • 空间增长

使用 append 实现增量更新带来了一个新的问题,就是不断 append 的话,文件大小会增长得不可控。需要在性能和空间上做个折中。

以内存 pagesize 为单位申请空间,在空间用尽之前都是 append 模式;当 append 到文件末尾时,进行文件重整、key 排重,尝试序列化保存排重结果;

排重后空间还是不够用的话,将文件扩大一倍,直到空间足够。

-(BOOL)append:(NSData*)data {if (space >= data.length) {append(fd, data);} else {newData = unique(m_allKV);if (total_space >= newData.length) {write(fd, newData);} else {while (total_space < newData.length) {total_space *= 2;}ftruncate(fd, total_space);write(fd, newData);}}
}
  • 数据有效性

考虑到文件系统、操作系统都有一定的不稳定性, crc 校验,对无效数据进行甄别。在 iOS 微信现网环境上,有平均约 70万日次的数据校验不通过。

  • Android多进程访问

将 MMKV 迁移到 Android 平台之后,要支持多进程访问, iOS 不支持多进程。

  1. 性能:MMKV在性能方面表现更好。由于采用了内存映射技术,它可以直接在内存中读取和写入数据,减少了磁盘IO操作,因此读写速度更快。相比之下,SharedPreferences是基于XML文件存储的,读取和写入需要进行磁盘IO操作,速度较慢。

  2. 跨进程和跨线程支持:MMKV天然支持跨进程和跨线程的数据共享。多个进程或线程可以同时访问和修改MMKV中的数据,而无需额外的同步操作。而SharedPreferences的跨进程支持较差,需要进行额外的同步机制或使用ContentProvider等方式才能实现跨进程共享。

  3. 存储容量:MMKV支持更大的存储容量。SharedPreferences将所有数据都存储在一个XML文件中,如果数据较多,读取和解析整个文件可能会影响性能。而MMKV将数据划分为多个固定大小的内存页,可以高效地读取和写入大量数据。

  4. 序列化和加密:MMKV提供了数据的序列化和加密功能。它可以将复杂的数据结构序列化为字节数组进行存储,并支持对数据进行加密保护。而SharedPreferences只能存储基本数据类型,对于复杂的数据结构需要进行手动的序列化和反序列化操作。灵活性和易用性:MMKV提供了更灵活和易用的API。它的API设计更加简洁,使用起来更方便,支持链式调用和类型安全。同时,MMKV还提供了一些额外的功能,如数据的版本控制、数据迁移和备份等。

  5. import com.tencent.mmkv.MMKV;public class MMKVExample {public static void main(String[] args) {// 初始化MMKVString rootDir = MMKV.initialize("path_to_directory");// 获取MMKV实例MMKV mmkv = MMKV.defaultMMKV();// 存储数据mmkv.putString("key1", "value1");mmkv.putInt("key2", 123);mmkv.putBoolean("key3", true);// 读取数据String value1 = mmkv.getString("key1", "");int value2 = mmkv.getInt("key2", 0);boolean value3 = mmkv.getBoolean("key3", false);System.out.println("Value 1: " + value1);System.out.println("Value 2: " + value2);System.out.println("Value 3: " + value3);}
    }
    dependencies {implementation 'com.tencent:mmkv:1.0.23'// replace "1.0.23" with any available version
    }

    支持从SP迁移数据importFromSharedPreferences

    MMKV 还额外实现了一遍 SharedPreferences、SharedPreferences.Editor 这两个 interface。

    // 可以跟SP用法一样
    SharedPreferences.Editor editor = mmkv.edit();
    // 无需调用 commit()
    //editor.commit();

    MMKV 的使用非常简单,所有变更立马生效,无需调用 sync、apply。 在 App 启动时初始化 MMKV,设定 MMKV 的根目录(files/mmkv/),例如在 MainActivity 里:

    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);String rootDir = MMKV.initialize(this);System.out.println("mmkv root: " + rootDir);//……
    }

    MMKV 提供一个全局的实例,可以直接使用:

    import com.tencent.mmkv.MMKV;
    //……MMKV kv = MMKV.defaultMMKV();kv.encode("bool", true);
    boolean bValue = kv.decodeBool("bool");kv.encode("int", Integer.MIN_VALUE);
    int iValue = kv.decodeInt("int");kv.encode("string", "Hello from mmkv");
    String str = kv.decodeString("string");

    使用后:

public native void clearAll();// MMKV's size won't reduce after deleting key-values// call this method after lots of deleting f you care about disk usage// note that `clearAll` has the similar effect of `trim`public native void trim();// call this method if the instance is no longer needed in the near future// any subsequent call to the instance is undefined behaviorpublic native void close();// call on memory warning// any subsequent call to the instance will load all key-values from file againpublic native void clearMemoryCache();// you don't need to call this, really, I mean it// unless you care about out of batterypublic void sync() {sync(true);}
  • 限制

一个键会存入多分实例,最后存入的就是最新的。

MMKV 在大部分情况下都性能强劲,key/value 的数量和长度都没有限制。

然而 MMKV 在内存里缓存了所有的 key-value,在总大小比较大的情况下(例如 100M+),App 可能会爆内存,触发重整回写时,写入速度也会变慢。

锁 lock unlock tryLock

注意如果一个进程lock住,另一个进程mmkvWithID获取MMKV时就阻塞住,直到持有进程释放。

// get the lock immediatelyMMKV mmkv2 = MMKV.mmkvWithID(LOCK_PHASE_2, MMKV.MULTI_PROCESS_MODE);mmkv2.lock();Log.d("locked in child", LOCK_PHASE_2);Runnable waiter = new Runnable() {@Overridepublic void run() {//阻塞住 直到其他进程释放MMKV mmkv1 = MMKV.mmkvWithID(LOCK_PHASE_1, MMKV.MULTI_PROCESS_MODE);mmkv1.lock();Log.d("locked in child", LOCK_PHASE_1);}};

如果其他进程有进行修改,不会立即触发onContentChangedByOuterProcess,

checkLoadData如果变化,会clearMemoryState,重新loadFromFile。//数据量大时不要太频繁

读取decodeXXX会阻塞住,先回调onContentChangedByOuterProcess,再返回值,保证值是最新的。

  • Binder MMAP

Binder MMAP是Android系统中的一种机制,用于在跨进程通信(IPC)中传输大型数据或共享内存区域。

在Android中,进程间通信主要通过Binder框架实现。Binder框架使用Binder驱动程序在不同的进程之间建立通信通道。通常情况下,进程间通信是通过传输小型的数据结构,如整数、字符串等。然而,当需要传输大量数据或者共享内存时,效率会受到限制。

为了解决这个问题,Android引入了Binder MMAP机制。Binder MMAP允许进程通过内存映射(MMAP)的方式共享内存区域,从而实现高效的数据传输。它通过以下步骤实现:

  1. 发送端将数据写入内存区域:发送端将要传输的数据写入一个内存区域,该内存区域通过MMAP映射到物理内存中。

  2. 发送端将内存区域的描述符发送给接收端:发送端将内存区域的描述符(文件描述符)通过Binder传递给接收端。

  3. 接收端获取内存区域描述符并映射到自己的地址空间:接收端通过Binder接收内存区域的描述符,并将其映射到自己的地址空间。

  4. 接收端从内存区域读取数据:接收端可以直接从内存区域中读取发送端写入的数据,而无需进行数据拷贝。

Binder MMAP的优点:

  1. 高效的数据传输:通过内存映射的方式,避免了数据拷贝和序列化/反序列化操作,提高了数据传输的效率。

  2. 支持大型数据和共享内存:适用于传输或共享大量数据或大型内存区域的场景,可以减少内存占用和提高性能。

  3. 跨进程通信:作为Android的进程间通信机制,支持不同进程之间的数据传输,方便实现跨进程功能。

Binder MMAP的缺点:

  1. 复杂性:相比较其他传输方式,使用Binder MMAP需要更多的代码和配置,对开发者来说可能需要更多的学习和理解。

  2. 依赖Binder框架:作为Android系统的一部分,使用Binder MMAP需要依赖于Binder框架,需要遵循Binder框架的规范和约束。

MMKV的优点:

  1. 高性能:相对于SharedPreferences等传统存储方式,MMKV具有更好的读写性能,特别是在高并发操作下表现更出色。

  2. 跨进程支持:MMKV支持跨进程访问,多个进程可以同时读写同一个MMKV实例,方便实现进程间数据共享。

  3. 功能丰富:MMKV提供了丰富的功能,如加密、压缩等,可以满足不同的数据存储需求。

MMKV的缺点:

  1. 存储大小限制:MMKV存储的总大小受限于设备的存储空间,如果存储的数据量较大,可能会占用较多的存储空间。

  2. 适用性受限:由于MMKV是针对键值存储而设计的,适用于存储简单的键值对数据,不适合存储复杂的数据结构。

  • 比sp快的原因

  1. 内存映射(Memory Mapping):MMKV使用了内存映射的技术,将数据直接映射到内存中,而不是像SharedPreferences一样将数据写入磁盘文件。这种内存映射的方式避免了频繁的磁盘读写操作,减少了IO开销,从而提高了读写性能。

  2. 零拷贝(Zero-copy):MMKV利用了内存映射的特性,实现了零拷贝的读写操作。当读取或写入数据时,MMKV直接在内存中进行操作,避免了数据的拷贝和序列化/反序列化操作,进一步提高了读写性能。

  3. 文件锁(File Locking):MMKV使用文件锁机制来保证多个进程对同一个MMKV实例的安全访问。这种文件锁机制可以有效地控制并发访问,避免了数据冲突和竞争条件,提高了并发操作的性能。

  4. 自定义序列化(Custom Serialization):MMKV使用自定义的序列化方式,将数据以二进制的形式存储,而不是SharedPreferences中的XML格式。这种自定义的序列化方式更加高效,减少了存储和解析数据的开销,提高了读写性能。

  • MMKV在android中的使用(替换sp)

在项目根目录下的 build.gradle 文件中加入

dependencies {implementation 'com.tencent:mmkv-static:1.2.10'
}

在项目 app 模块下的 build.gradle 文件中加入 

buildscript {repositories {mavenCentral()//这行依赖}
}
allprojects {repositories {mavenCentral()//这行依赖}
}

在Application中初始化

MMKV.initialize(this)

MMKV 默认把文件存放在$(FilesDir)/mmkv/目录。你可以在 MMKV初始化时自定义根目录:

String dir = getFilesDir().getAbsolutePath() + "/mmkv";
String rootDir = MMKV.initialize(dir);

Kotlin中使用

import com.tencent.mmkv.MMKV;
//……//1. 获取默认全局实例 (与下面的几选一,一般就使用这个就行)
var mmkv: MMKV = MMKV.defaultMMKV()//2. 也可以自定义MMKV对象,设置自定ID  (根据业务区分的存取实例)
var mmkv: MMKV = MMKV.mmkvWithID("ID")//3. MMKV默认是支持单进程的,如果业务需要多进程访问,需要在初始化的时候添加多进程模式参数
var mmkv = MMKV.mmkvWithID("ID", MMKV.MULTI_PROCESS_MODE)  //多进程同步支持

存取方法

// 添加/更新数据
mmkv?.encode(key, value);// 获取数据
int value = mmkv.decodeInt(key);
String value = mmkv.decodeString(key);
//...获取等类型// 删除数据
mmkv.removeValueForKey(key);

如果需要存取对象,可以用存取对象json字符串的方法,将对象转成json存,取出json转回对象。

SP迁移

MMKV可以调用importFromSharedPreferences方法进行SP的数据迁移,示例代码如下: MMKV实现了SharedPreferences,Editor两个接口,所以在迁移之后SP的操作代码可以不用更改。

val mmkv = MMKV.mmkvWithID("myData")
val olderData = DemoApplication.mContext?.getSharedPreferences("myData", MODE_PRIVATE)
mmkv?.importFromSharedPreferences(olderData)
olderData?.edit()?.clear()?.apply()
  • Java中的使用

MMKV 提供一个全局的实例,可以直接使用

import com.tencent.mmkv.MMKV;
//……//1. 获取默认全局实例 (一般就使用这个就行)
MMKV kv = MMKV.defaultMMKV();//2. 也可以自定义MMKV对象,设置自定ID  (根据业务区分的存取实例)
MMKV kv = MMKV.mmkvWithID("ID");//3. MMKV默认是支持单进程的,如果业务需要多进程访问,需要在初始化的时候添加多进程模式参数
MMKV kv = MMKV.mmkvWithID("ID", MMKV.MULTI_PROCESS_MODE); //多进程同步支持

存取方法

/** 添加/更新数据 **/
//存boolean类型
kv.encode("bool", true);
//存int类型
kv.encode("int", Integer.MIN_VALUE);
//存string类型
kv.encode("string", "MyiSMMKV");/** 获取数据 **/
//获取boolean类型数据
boolean bValue = kv.decodeBool("bool");
//获取int类型数据
int iValue = kv.decodeInt("int");
//获取string类型数据
String str = kv.decodeString("string");
//...等类型的获取// 删除数据
mmkv.removeValueForKey(key);

如果需要存取对象,可以用存取对象json字符串的方法,将对象转成json存,取出json转回对象。

SP迁移

MMKV kv = MMKV.mmkvWithID("myData");
SharedPreferences olderData = App.getInstance().getSharedPreferences("myData", MODE_PRIVATE);
kv.importFromSharedPreferences(olderData);
olderData.edit().clear().apply();

相关文章:

MMKV(1)

内存准备 通过 mmap 内存映射文件&#xff0c;提供一段可供随时写入的内存块&#xff0c;App 只管往里面写数据&#xff0c;由操作系统负责将内存回写到文件&#xff0c;不必担心 crash 导致数据丢失。 数据组织 数据序列化方面选用 protobuf 协议&#xff0c;pb 在性能和空…...

centos 7.9 源码安装htop

1.下载源码 wget http://sourceforge.net/projects/htop/files/latest/download 2.上传到tmp目录&#xff0c;并解压 tar xvzf htop-1.0.2.tar.gz mv htop-1.0.2 /opt/ 进入到 cd /opt/htop-1.0.2/ 3.编译并安装 ./configure && make && make install 4.…...

Element UI之Button 按钮

Button 按钮 常用的操作按钮。 按需引入方式 如果是完整引入可跳过此步骤 import Vue from vue import { Button } from element-ui import element-ui/lib/theme-chalk/base.css import element-ui/lib/theme-chalk/button.css import element-ui/lib/theme-chalk/icon.cs…...

dig 简明教程

哈喽大家好&#xff0c;我是咸鱼 不知道大家在日常学习或者工作当中用 dig 命令多不多 dig 是 Domain Information Groper 的缩写&#xff0c;对于网络管理员和在域名系统(DNS)领域工作的小伙伴来说&#xff0c;它是一个非常常见且有用的工具。 无论是简单的 DNS 解析查找还…...

深度分析AMQP以及在rabbitMQ中的应用

文章目录 AMQP是什么AMQP在rabbitMQ中的应用AMQP协议的三层AMQP的三大组件AMQP的连接信道RabbitMQ 如何实现信道&#xff1a; AMQP是什么 AMQP&#xff08;Advanced Message Queuing Protocol&#xff09;是一种开放标准的消息队列协议。它提供了一个统一的、可靠的、异步的消…...

GB/T 28627-2023 抹灰石膏检测

抹灰石膏是指以半水石膏、Ⅱ型无水石膏单独或两者混合后作为主要胶凝材料&#xff0c;掺入集料和外加剂制成的用于建筑物室内墙面和顶棚基底抹灰找平用的石膏砂浆。 GB/T 28627-2023抹灰石膏检测项目&#xff1a; 测试项目 测试方法 凝结时间 GB/T 28627 保水率 GB/T 286…...

JDK版本和Gradle版本配套关系

Java versionSupport for compiling/testing/…​Support for running Gradle 8 N/A 2.0 9 N/A 4.3 10 N/A 4.7 11 N/A 5.0 12 N/A 5.4 13 N/A 6.0 14 N/A 6.3 15 6.7 6.7 16 7.0 7.0 17 7.3 7.3 18 7.5 7.5 19 7.6 7.6 20 8.1 8.3 21 …...

在Linux中,怎么查看自己电脑的系统架构是什么?

2023年10月18日&#xff0c;周三晚上 这些命令会返回一个字符串&#xff0c;表示系统的架构。 常见的架构包括 x86&#xff08;32位&#xff09;、x86_64&#xff08;64位&#xff09;、ARM 等。 方法1&#xff1a;使用uname命令 uname -m方法2&#xff1a;使用arch命令 ar…...

自5月以来,俄罗斯Sandworm黑客侵入了11家乌克兰电信公司

导语&#xff1a;据乌克兰计算机应急响应团队&#xff08;CERT-UA&#xff09;的最新报告称&#xff0c;自2023年5月至9月&#xff0c;俄罗斯政府支持的黑客组织Sandworm成功侵入了乌克兰的11家电信服务提供商。这一组织被认为与俄罗斯武装部队的GRU有关。 简介 根据乌克兰计算…...

怎样做好接口自动化测试?

今天介绍一下在接口自动化测试相关实践中总结到的一些经验。 接口自动化测试的目的 自动化测试的主要目的是用来回归测试的&#xff0c;当代码有变化时&#xff0c;有可能影响不应该变化的逻辑&#xff0c;这个时候为了确认这种情况&#xff0c;就需要进行回归测试。有时候回…...

Leetcode刷题详解——找到字符串中所有字母异位词

1. 题目链接&#xff1a;438. 找到字符串中所有字母异位词 2. 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找到 s 中所有 p 的 异位词 的子串&#xff0c;返回这些子串的起始索引。不考虑答案输出的顺序。 异位词 指由相同字母重排列形成的字符串&#xff08;包括…...

Android 自定义view 圆形进度条

Android 自定义view 圆形进度条 前言一、码前分析二、开码1.画笔2.弧度3.圆弧的位置4.暴露给外部设置进度条的方法三、使用四、完整代码 总结 前言 先来看看效果&#xff0c;大概要实现这么一个圆形的进度条 一、码前分析 要实现这么一个进度条的效果&#xff0c;实际上是要画…...

混凝土基础的智能设计:VisualFoundation 12.0 Crack

实现混凝土基础的智能设计:工程师依靠 VisualFoundation:使用这个专注的工具可以更轻松、更强大地对基础进行建模。通用 FEA 工具&#xff08;如VisualAnalysis&#xff09;可以做很多事情&#xff0c;但对于特定于基础的工程来说&#xff0c;这更快、更智能。 草图边界 快速绘…...

C++中成员函数的重载覆盖与隐藏

1.重载与覆盖 重载&#xff1a;成员函数被重载的特征&#xff1a;在同一个类中&#xff0c;函数名相同&#xff0c;参数不同&#xff0c;vritual关键字可有可无。 覆盖&#xff1a;覆盖是指派生类函数覆盖基类函数&#xff0c;特征是&#xff1a;在有继承关系的类中&#xff0…...

电子器件系列49:CD4050B缓冲器

同相和反向缓冲器 还搞不懂缓冲电路&#xff1f;看这一文&#xff0c;工作原理作用电路设计使用方法 - 知乎 (zhihu.com) 缓冲器_百度百科 (baidu.com) 1、缓冲器的定义 缓冲器是数字元件的其中一种&#xff0c;它对输入值不执行任何运算&#xff0c;其输出值和输入值一样&…...

Leetcode 349 两个数组的交集 (哈希表)

Leetcode 349 两个数组的交集 &#xff08;哈希表&#xff09; 解法1 &#x1f60b;解法2 解法1 &#x1f60b; 自己的笨比方法:【哇这居然是标准解法之一&#xff0c;我不是笨比&#x1f60b;&#x1f60b;&#x1f60b;】 创建了两个hash数组&#xff0c;nums1出现一个就对应…...

基于YOLOv8模型的水下目标检测系统(PyTorch+Pyside6+YOLOv8模型)

摘要&#xff1a;基于YOLOv8模型的水下目标检测系统可用于日常生活中检测与定位鱼、水母、企鹅、海鹦、鲨鱼、海星、黄貂鱼&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的目标检测&#xff0c;另外本系统还支持图片、视频等格式的结果可视化与结果导出。本系统…...

vue-cli脚手架创建项目时报错Error: command failed: npm install --loglevel error

项目背景 环境&#xff1a;vue-cli 5.x 在工程文件中&#xff0c;后端模块wms已经创建完成&#xff0c;现在想新建一个名为vue-web的前端模块 执行命令vue create vue-web时&#xff0c; 报错Error: command failed: npm install --loglevel error 问题分析及解决 排查过程…...

c语言练习92:链表的中间结点

链表的中间结点 链表的结点为空时无法访问其next成员否则会报错 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/typedef struct ListNode ListNode; struct ListNode* middleNode(struct ListNode* head){if(h…...

CentOS(4)——关于Linux软件下载时:amd64、x86、x86_64、arm64 的说明

目录 一、简介 二、常见的CPU架构 三、Linux查看CPU架构命令 ①arch命令 ②uname -a 命令 ③lscpu 一、简介 在安装GitLab Runner的时候&#xff0c;去清华源下载RPM包时发现同一个软件有许多不同架构的安装包&#xff0c;常见的有amd64、x86、x86_64、arm64这些架构&am…...

【人工智能】deepseek七篇论文阅读笔记大纲

七篇文章看了整整五天&#xff0c;加上整理笔记和问ds优化&#xff0c;大致的框架是有了。具体的公式细节比较多&#xff0c;截图也比较麻烦&#xff0c;就不列入大纲去做笔记了。 DeepSeek-LLM&#xff1a;一切的起点&#xff0c;所以探索的东西比较多&#xff0c;包括&#x…...

【机器学习基础】机器学习入门核心算法:随机森林(Random Forest)

机器学习入门核心算法&#xff1a;随机森林&#xff08;Random Forest&#xff09; 1. 算法逻辑2. 算法原理与数学推导2.1 核心组件2.2 数学推导2.3 OOB&#xff08;Out-of-Bag&#xff09;误差 3. 模型评估评估指标特征重要性可视化 4. 应用案例4.1 医疗诊断4.2 金融风控4.3 遥…...

【入门】【练9.3】 加四密码

| 时间限制&#xff1a;C/C 1000MS&#xff0c;其他语言 2000MS 内存限制&#xff1a;C/C 64MB&#xff0c;其他语言 128MB 难度&#xff1a;中等 分数&#xff1a;100 OI排行榜得分&#xff1a;12(0.1*分数2*难度) 出题人&#xff1a;root | 描述 要将 China…...

开源的JT1078转GB28181服务器

JT1078转GB28181流程 项目地址&#xff1a; JT1078转GB28181的流媒体服务器: https://github.com/lkmio/lkm JT1078转GB28181的信令服务器: https://github.com/lkmio/gb-cms 1. 创建GB28181 UA 调用接口: http://localhost:9000/api/v1/jt/device/add 请求体如下&#xf…...

树欲静而风不止,子欲养而亲不待

2025年6月2日&#xff0c;13~26℃&#xff0c;一般 待办&#xff1a; 物理2 、物理 学生重修 职称材料的最后检查 教学技能大赛PPT 遇见&#xff1a;使用通义创作了一副照片&#xff0c;很好看&#xff01;都有想用来创作自己的头像了&#xff01; 提示词如下&#xff1a; A b…...

免费酒店管理系统+餐饮系统+小程序点餐——仙盟创梦IDE

酒店系统主屏幕 房间管理 酒店管理系统的房间管理&#xff0c;可实现对酒店所有房间的实时掌控。它能清晰显示房间状态&#xff0c;如已预订、已入住、空闲等&#xff0c;便于高效安排入住与退房&#xff0c;合理分配资源&#xff0c;提升服务效率&#xff0c;保障酒店运营有条…...

2025年机械化设计制造与计算机工程国际会议(MDMCE 2025)

2025年机械化设计制造与计算机工程国际会议&#xff08;MDMCE 2025&#xff09; 2025 International Conference on Mechanized Design, Manufacturing, and Computer Engineering 一、大会信息 会议简称&#xff1a;MDMCE 2025 大会地点&#xff1a;中国贵阳 审稿通知&#…...

第十二节:第五部分:集合框架:Set集合的特点、底层原理、哈希表、去重复原理

Set系列集合特点 哈希值 HashSet集合的底层原理 HashSet集合去重复 代码 代码一&#xff1a;整体了解一下Set系列集合的特点 package com.itheima.day20_Collection_set;import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; import java.util.…...

企业级 AI 开发新范式:Spring AI 深度解析与实践

一、Spring AI 的核心架构与设计哲学 1.1 技术定位与价值主张 Spring AI 作为 Spring 生态系统的重要组成部分&#xff0c;其核心使命是将人工智能能力无缝注入企业级 Java 应用。它通过标准化的 API 抽象和 Spring Boot 的自动装配机制&#xff0c;让开发者能够以熟悉的 Spr…...

代谢组数据分析(二十六):LC-MS/MS代谢组学和脂质组学数据的分析流程

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包依赖包安装包加载需要的R包数据下载以及转换mzML数据预处理代谢物注释LipidFinder过滤MultiABLER数据预处理过滤补缺失值对数变换数据标准化下游数据分析总结系统信息参考介…...