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

大数据-玩转数据-Flink窗口

一、Flink 窗口 理解

在流处理应用中,数据是连续不断的,因此我们不可能等到所有数据都到了才开始处理。当然我们可以每来一个消息就处理一次,但是有时我们需要做一些聚合类的处理,例如:在过去的1分钟内有多少用户点击了我们的网页。在这种情况下,我们必须定义一个窗口,用来收集最近一分钟内的数据,并对这个窗口内的数据进行计算。

流式计算是一种被设计用于处理无限数据集的数据处理引擎,而无限数据集是指一种不断增长的本质上无限的数据集,而Window窗口是一种切割无限数据为有限块进行处理的手段。

在Flink中, 窗口(window)是处理无界流的核心. 窗口把流切割成有限大小的多个"存储桶"(bucket), 我们在这些桶上进行计算.

时间窗口
时间窗口包含一个开始时间戳(包括)和结束时间戳(不包括), 这两个时间戳一起限制了窗口的尺寸。在代码中, Flink使用TimeWindow这个类来表示基于时间的窗口. 这个类提供了key查询开始时间戳和结束时间戳的方法, 还提供了针对给定的窗口获取它允许的最大时间戳的方法(maxTimestamp()),时间窗口又分3种:滚动窗口、滑动窗口、会话窗口。

二、数据准备

准备一个WaterSensor类方便演示

package com.lyh.bean;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@NoArgsConstructor
@AllArgsConstructor
public class WaterSensor {private String id;private Long ts;private Integer vc;
}

三、时间滚动窗口

滚动窗口有固定的大小, 窗口与窗口之间不会重叠也没有缝隙.比如,如果指定一个长度为5分钟的滚动窗口, 当前窗口开始计算, 每5分钟启动一个新的窗口。滚动窗口能将数据流切分成不重叠的窗口,每一个事件只能属于一个窗口。

滚动窗口有固定的大小, 窗口与窗口之间不会重叠也没有缝隙.比如,如果指定一个长度为5分钟的滚动窗口, 当前窗口开始计算, 每5分钟启动一个新的窗口.
滚动窗口能将数据流切分成不重叠的窗口,每一个事件只能属于一个窗口
1.时间间隔可以通过: Time.milliseconds(x), Time.seconds(x), Time.minutes(x),等等来指定,2.我们传递给window函数的对象叫窗口分配器.

时间滚动窗口代码

package com.lyh.flink07;import com.lyh.bean.WaterSensor;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.windowing.ProcessWindowFunction;
import org.apache.flink.streaming.api.windowing.assigners.TumblingProcessingTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.api.windowing.windows.TimeWindow;
import org.apache.flink.util.Collector;import java.util.ArrayList;
import java.util.List;public class Window_s {public static void main(String[] args) throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();env.setParallelism(1);env.socketTextStream("hadoop100",9999).map(line -> {String[] data = line.split(",");return new WaterSensor(data[0],Long.valueOf(data[1]),Integer.valueOf(data[2]));}).keyBy(WaterSensor::getId).window(TumblingProcessingTimeWindows.of(Time.seconds(5))).process(new ProcessWindowFunction<WaterSensor, String,String, TimeWindow>() {@Overridepublic void process(String key,Context ctx,Iterable<WaterSensor> elements,Collector<String> out) throws Exception {List<WaterSensor> list  = toList(elements);long starttime = ctx.window().getStart();long endtime = ctx.window().getEnd();out.collect("窗口:" + starttime + "  " + endtime + "  " + "key:" + key + "  " + "list:" + list);}}).print();env.execute();}private static <T>List<T> toList(Iterable<T> it) {List<T>  list = new ArrayList<>();for (T t : it) {list.add(t);}return list;}
}

运行结果
在hadoop100 服务器
输入nc -lk 999 启动socket
在这里插入图片描述

消费结果:
在这里插入图片描述

四、时间滑动窗口

与滚动窗口一样, 滑动窗口也是有固定的长度. 另外一个参数我们叫滑动步长, 用来控制滑动窗口启动的频率.
所以, 如果滑动步长小于窗口长度, 滑动窗口会重叠. 这种情况下, 一个元素可能会被分配到多个窗口中
例如, 滑动窗口长度10分钟, 滑动步长5分钟, 则, 每5分钟会得到一个包含最近10分钟的数据。

时间滑动窗口代码

package com.lyh.flink07;import com.lyh.bean.WaterSensor;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.windowing.ProcessWindowFunction;
import org.apache.flink.streaming.api.windowing.assigners.SlidingProcessingTimeWindows;
import org.apache.flink.streaming.api.windowing.assigners.TumblingProcessingTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.api.windowing.windows.TimeWindow;
import org.apache.flink.util.Collector;import java.util.ArrayList;
import java.util.List;public class Window_s {public static void main(String[] args) throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();env.setParallelism(1);env.socketTextStream("hadoop100",9999).map(line -> {String[] data = line.split(",");return new WaterSensor(data[0],Long.valueOf(data[1]),Integer.valueOf(data[2]));}).keyBy(WaterSensor::getId)
//                .window(TumblingProcessingTimeWindows.of(Time.seconds(5))).window(SlidingProcessingTimeWindows.of(Time.seconds(5),Time.seconds(2))).process(new ProcessWindowFunction<WaterSensor, String,String, TimeWindow>() {@Overridepublic void process(String key,Context ctx,Iterable<WaterSensor> elements,Collector<String> out) throws Exception {List<WaterSensor> list  = toList(elements);long starttime = ctx.window().getStart();long endtime = ctx.window().getEnd();out.collect("窗口:" + starttime + "  " + endtime + "  " + "key:" + key + "  " + "list:" + list);}}).print();env.execute();}private static <T>List<T> toList(Iterable<T> it) {List<T>  list = new ArrayList<>();for (T t : it) {list.add(t);}return list;}
}

执行结果
在hadoop100 服务器
输入nc -lk 999 启动socket
在这里插入图片描述
消费结果
在这里插入图片描述

五、时间会话窗口

会话窗口分配器会根据活动的元素进行分组. 会话窗口不会有重叠, 与滚动窗口和滑动窗口相比, 会话窗口也没有固定的开启和关闭时间.
如果会话窗口有一段时间没有收到数据, 会话窗口会自动关闭, 这段没有收到数据的时间就是会话窗口的gap(间隔)
我们可以配置静态的gap, 也可以通过一个gap extractor 函数来定义gap的长度. 当时间超过了这个gap, 当前的会话窗口就会关闭, 后序的元素会被分配到一个新的会话窗口。

时间会话窗口代码

package com.lyh.flink07;import com.lyh.bean.WaterSensor;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.windowing.ProcessWindowFunction;
import org.apache.flink.streaming.api.windowing.assigners.ProcessingTimeSessionWindows;
import org.apache.flink.streaming.api.windowing.assigners.SlidingProcessingTimeWindows;
import org.apache.flink.streaming.api.windowing.assigners.TumblingProcessingTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.api.windowing.windows.TimeWindow;
import org.apache.flink.util.Collector;import java.util.ArrayList;
import java.util.List;public class Window_s {public static void main(String[] args) throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();env.setParallelism(1);env.socketTextStream("hadoop100",9999).map(line -> {String[] data = line.split(",");return new WaterSensor(data[0],Long.valueOf(data[1]),Integer.valueOf(data[2]));}).keyBy(WaterSensor::getId)
//                .window(TumblingProcessingTimeWindows.of(Time.seconds(5)))
//                .window(SlidingProcessingTimeWindows.of(Time.seconds(5),Time.seconds(2))).window(ProcessingTimeSessionWindows.withGap(Time.seconds(3))).process(new ProcessWindowFunction<WaterSensor, String,String, TimeWindow>() {@Overridepublic void process(String key,Context ctx,Iterable<WaterSensor> elements,Collector<String> out) throws Exception {List<WaterSensor> list  = toList(elements);long starttime = ctx.window().getStart();long endtime = ctx.window().getEnd();out.collect("窗口:" + starttime + "  " + endtime + "  " + "key:" + key + "  " + "list:" + list);}}).print();env.execute();}private static <T>List<T> toList(Iterable<T> it) {List<T>  list = new ArrayList<>();for (T t : it) {list.add(t);}return list;}
}

运行结果

在hadoop100 服务器
输入nc -lk 999 启动socket
在这里插入图片描述
消费结果
在这里插入图片描述
因为会话窗口没有固定的开启和关闭时间, 所以会话窗口的创建和关闭与滚动,滑动窗口不同. 在Flink内部, 每到达一个新的元素都会创建一个新的会话窗口, 如果这些窗口彼此相距比较定义的gap小, 则会对他们进行合并. 为了能够合并, 会话窗口算子需要合并触发器和合并窗口函数: ReduceFunction, AggregateFunction, or ProcessWindowFunction 。

六、基于元素个数的滚动窗口

默认的CountWindow是一个滚动窗口,只需要指定窗口大小即可,当元素数量达到窗口大小时,就会触发窗口的执行。
实例代码
.countWindow(3)
说明:哪个窗口先达到3个元素, 哪个窗口就关闭. 不影响其他的窗口.

基于元素个数的滚动窗口代码

package com.lyh.flink07;import com.lyh.bean.WaterSensor;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.windowing.ProcessWindowFunction;
import org.apache.flink.streaming.api.windowing.windows.GlobalWindow;
import org.apache.flink.util.Collector;import java.util.ArrayList;
import java.util.List;public class Window_s_n {public static void main(String[] args) throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();env.setParallelism(1);env.socketTextStream("hadoop100",9999).map(line -> {String[] data = line.split(",");return new WaterSensor(data[0],Long.valueOf(data[1]),Integer.valueOf(data[2]));}).keyBy(WaterSensor::getId).countWindow(2).process(new ProcessWindowFunction<WaterSensor, String, String, GlobalWindow>() {@Overridepublic void process(String key,Context ctx,Iterable<WaterSensor> elements,Collector<String> out) throws Exception {List<WaterSensor> list  = toList(elements);out.collect("窗口:" + "key:" + key + "  " + "list:" + list);}}).print();env.execute();}private static <T>List<T> toList(Iterable<T> it) {List<T>  list = new ArrayList<>();for (T t : it) {list.add(t);}return list;}
}

运行结果
在这里插入图片描述
在这里插入图片描述

七、基于元素个数的滑动窗口

滑动窗口和滚动窗口的函数名是完全一致的,只是在传参数时需要传入两个参数,一个是window_size,一个是sliding_size。下面代码中的sliding_size设置为了2,也就是说,每收到两个相同key的数据就计算一次,每一次计算的window范围最多是3个元素。
实例代码
.countWindow(3, 2)

package com.lyh.flink07;import com.lyh.bean.WaterSensor;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.windowing.ProcessWindowFunction;
import org.apache.flink.streaming.api.windowing.windows.GlobalWindow;
import org.apache.flink.util.Collector;import java.util.ArrayList;
import java.util.List;public class Window_s_n {public static void main(String[] args) throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();env.setParallelism(1);env.socketTextStream("hadoop100",9999).map(line -> {String[] data = line.split(",");return new WaterSensor(data[0],Long.valueOf(data[1]),Integer.valueOf(data[2]));}).keyBy(WaterSensor::getId)
//                .countWindow(2).countWindow(3,2).process(new ProcessWindowFunction<WaterSensor, String, String, GlobalWindow>() {@Overridepublic void process(String key,Context ctx,Iterable<WaterSensor> elements,Collector<String> out) throws Exception {List<WaterSensor> list  = toList(elements);out.collect("窗口:" + "key:" + key + "  " + "list:" + list);}}).print();env.execute();}private static <T>List<T> toList(Iterable<T> it) {List<T>  list = new ArrayList<>();for (T t : it) {list.add(t);}return list;}
}

运行结果
在这里插入图片描述
在这里插入图片描述

相关文章:

大数据-玩转数据-Flink窗口

一、Flink 窗口 理解 在流处理应用中&#xff0c;数据是连续不断的&#xff0c;因此我们不可能等到所有数据都到了才开始处理。当然我们可以每来一个消息就处理一次&#xff0c;但是有时我们需要做一些聚合类的处理&#xff0c;例如&#xff1a;在过去的1分钟内有多少用户点击…...

【python爬虫】—豆瓣电影Top250

豆瓣电影Top250 豆瓣榜单简介需求描述Python实现 豆瓣榜单简介 豆瓣电影 Top 250 榜单是豆瓣网站上列出的评分最高、受观众喜爱的电影作品。这个榜单包含了一系列优秀的影片&#xff0c;涵盖了各种类型、不同国家和时期的电影。 需求描述 使用python爬取top250电影&#xff…...

【跟小嘉学 Rust 编程】十五、智能指针

系列文章目录 【跟小嘉学 Rust 编程】一、Rust 编程基础 【跟小嘉学 Rust 编程】二、Rust 包管理工具使用 【跟小嘉学 Rust 编程】三、Rust 的基本程序概念 【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念 【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据 【跟小嘉学…...

Python爬虫基础之正则表达式

目录 一、什么是正则表达式&#xff1f; 二、re.compile()编译函数 三、group()获取匹配结果函数 四、常用匹配规则 4.1匹配单个字符 4.2匹配前字符次数 4.3匹配原生字符串 4.4匹配字符串开头和结尾 4.5分组匹配 五、re.match()开头匹配函数 六、re.search()全文搜索…...

【LeetCode】双指针妙解有效三角形的个数

Problem: 611. 有效三角形的个数 文章目录 题目分析讲解算法原理复杂度Code 题目分析 首先我们来分析一下本题的思路 看到题目中给出的示例 题目的意思很简单&#xff0c;就是将给到的数字去做一个组合&#xff0c;然后看看这三条边是否可以构成三角形。那判断的方法不用我说&a…...

mysql 计算两点之间距离

先说一下我们可能会用到的一些场景&#xff0c;这样同学们可以先评估&#xff0c;该篇文章是否对你有帮助&#xff01; 场景&#xff1a; 假设 美团&#xff0c;我点外卖时&#xff0c;系统会让我先进行定位&#xff0c;比如我定位在了 A 点&#xff0c;系统就会给我推荐&…...

c语言自定义头文件是什么情况下使用?一般在什么情况下引用自定义的头文件?一般在自定义头文件中写什么代码?

c语言自定义头文件是什么情况下使用&#xff1f;一般在什么情况下引用自定义的头文件&#xff1f;一般在自定义头文件中写什么代码&#xff1f; C语言自定义头文件是一种用来封装函数和变量声明的文件&#xff0c;它通常用于将一组相关的函数和变量的声明集中在一个地方&#…...

electron应用打包成功纪念一下

electron应用打包成功纪念一下&#xff0c;以前曾经行过后来打包各种报错&#xff0c;现在有空就尝试解决一下 首先安装nvm能够方便切换node版本 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash 顺利安装后你用nvm list查看node列表时会…...

远程办公中安全远程访问解决方案

什么是安全远程访问 安全的远程访问是一个至关重要的过程&#xff0c;可让您使用互联网从远处完全控制某人的设备。为了确保安全&#xff0c;为受保护的远程访问采取了额外的身份验证和加密措施。 为什么安全远程访问解决方案很重要 当 IT 技术人员从远处帮助人们解决计算机…...

StartUp启动框架-Android启动性能

简述 当谈论Android应用程序的启动性能时&#xff0c;StartUp启动框架是一个不可忽视的关键工具。它旨在优化应用程序的启动过程&#xff0c;确保用户在打开应用时能够迅速获得流畅、高效的体验。让我们来深入了解StartUp框架的作用和重要性&#xff0c;以及它是如何改善Andro…...

Positive Technologies:五分之四的网络攻击具有针对性

Positive Technologies 对 2023 年第二季度的相关网络威胁进行了分析。报告显示&#xff0c;自今年年初以来&#xff0c;有针对性的攻击数量增加了 10%&#xff0c;目前占 78%。专家们注意到利用漏洞的大规模攻击和大量用户个人数据的泄露。此外&#xff0c;在此期间&#xff0…...

clickhouse的另类表引擎

clickhouse常用的MergeTree引擎外&#xff0c;还有特殊的引擎 1&#xff0c;memory引擎&#xff0c;顾名思义&#xff0c;数据是存储在内存中&#xff0c;数据不会被压缩也不会倍格式化转换数据在内存中保存的形态与查询时看到的如出一辙&#xff0c;重启ck数据丢失 2&#xff…...

Uniapp新版本打包后覆盖安装,新增的页面无法跳转,需退出重新启动才可以打开的解决方案

最近写uniapp项目&#xff0c;发现一个坑&#xff0c;在新版本覆盖安装后直接打开APP&#xff0c;新增的页面竟然无法跳转&#xff0c;需要重新启动才可以正常打开&#xff0c;在网上查了很多方法&#xff0c;最终总结下来有以下几点&#xff1a; 1.看打的是debug包还是releas…...

系统架构设计高级技能 · 面向服务架构设计理论与实践

点击进入系列文章目录 系统架构设计高级技能 面向服务架构设计理论与实践 一、SOA的相关概念1.1SOA的定义1.2 业务流程与业务流程执行语言 二、SOA的发展史三、SOA与微服务的区别三、SOA的参考架构四、SOA的主要协议规范五、SOA的设计标准要求六、SOA的作用与设计原则七、SOA的…...

QT注册界面练习(信号与槽实现页面跳转)

一、注册界面练习思路以及具体代码 在完成注册页面搭建的前提下&#xff0c;通过信号与槽机制实现多组件之间的相互通信&#xff0c;实现页面跳转。 基本步骤&#xff1a; 首先&#xff0c;将注册页面的登录按钮与成功登陆信号绑定&#xff0c;当用户名与密码均匹配时&#xf…...

MySQL从入门到精通【进阶篇】之 主从复制详解

文章目录 0.前言1. 主从复制简介2. 主从复制的工作流程主从复制过程中的日志文件作用&#xff08;Binary Log&#xff09;和中继日志&#xff08;Relay Log&#xff09; 3. MySQL主从复制的配置4. 参考资料 0.前言 MySQL的主从复制和读写分离是数据库领域的基本概念&#xff0…...

vue使用qrcodejs2生成二维码

目录 概要 构建展示的vue组件qrcode.vue 组件的使用 概要 项目中用到需要展示二维码的样式&#xff0c;想到了qrcode 例如&#xff1a; 前提&#xff1a;安装包 npm install qrcodejs2 --save 构建展示的vue组件qrcode.vue <template><div style"width: …...

python注释

任何编程语言都少不了注释&#xff0c;Python也不例外&#xff0c;以下是Python注释的具体用法&#xff1a; 单行注释 Python编程语言的单行注释常以#开头&#xff0c;单行注释可以作为单独的一行放在被注释代码行之上&#xff0c;也可以放在语句或者表达式之后。 实例&…...

update-alternatives详解

1.功能作用 update-alternatives是dpkg的实用工具&#xff0c;用来维护系统命令的符号链接&#xff0c;以决定系统默认使用什么命令。 在Debian系统中&#xff0c;我们可能会同时安装有很多功能类似的程序和可选配置&#xff0c;如Web浏览器程序(firefox&#xff0c;konquero…...

JavaScript 编写更好的条件语句

在任何编程语言中&#xff0c;代码需要根据不同的条件在给定的输入中做不同的决定和执行相应的动作。 例如&#xff0c;在一个游戏中&#xff0c;如果玩家生命点为0&#xff0c;游戏结束。在天气应用中&#xff0c;如果在早上被查看&#xff0c;显示一个日出图片&#xff0c;如…...

嵌入式WiFi开发 | 基于wireless_tools的交叉编译实战与移植指南

1. 嵌入式WiFi开发入门&#xff1a;为什么需要wireless_tools&#xff1f; 在嵌入式Linux开发中&#xff0c;网络连接能力往往是刚需。想象一下你的智能家居设备需要自动连接路由器&#xff0c;或者工业传感器需要通过WiFi上传数据——这些都离不开可靠的无线网络配置工具。这就…...

手把手教你用GD32F30x的定时器搞定BLDC电机霍尔信号捕获(附完整代码)

手把手教你用GD32F30x的定时器实现BLDC电机霍尔信号精准捕获 当你的GD32F30x开发板已经连接好BLDC电机的霍尔传感器&#xff0c;却发现转速计算总是不准确时&#xff0c;问题往往出在定时器的配置细节上。本文将带你从寄存器层面拆解霍尔信号捕获的全流程&#xff0c;解决实际开…...

重新定义Windows桌面体验:Seelen UI如何让你告别千篇一律的界面

重新定义Windows桌面体验&#xff1a;Seelen UI如何让你告别千篇一律的界面 【免费下载链接】Seelen-UI The Fully Customizable Desktop Environment for Windows 10/11. 项目地址: https://gitcode.com/GitHub_Trending/se/Seelen-UI 厌倦了Windows千篇一律的桌面环境…...

极简音乐工具:重新定义纯粹听歌体验

极简音乐工具&#xff1a;重新定义纯粹听歌体验 【免费下载链接】tonzhon-music 铜钟 (Tonzhon.com): 免费听歌; 没有直播, 社交, 广告, 干扰; 简洁纯粹, 资源丰富, 体验独特&#xff01;(密码重置功能已回归) 项目地址: https://gitcode.com/GitHub_Trending/to/tonzhon-mus…...

gitru:一个由 Rust 打造的零依赖 Git 提交信息校验工具

gitru 基于 Git 的 commit-msg Hook 实现&#xff0c;用于在提交阶段自动校验提交信息格式。 在团队协作开发中&#xff0c;规范的 Git 提交信息是代码追溯、版本管理、自动生成变更日志的基础。 但现实往往是&#xff1a; 人工约束容易遗漏手动配置 Hook 繁琐提交信息格式随心…...

麒麟V10 x86_64系统下Qt 5.12.7安装全攻略(附常见报错解决方案)

麒麟V10 x86_64系统Qt 5.12.7深度安装指南与疑难攻坚 国产操作系统生态的崛起让越来越多的开发者开始关注麒麟平台。作为一款基于Linux的国产操作系统&#xff0c;麒麟V10在x86_64架构下的表现尤为出色。本文将带你全面掌握Qt 5.12.7在麒麟V10 x86_64系统上的安装与配置技巧&am…...

OpenRocket:开源火箭仿真软件的设计与分析全指南

OpenRocket&#xff1a;开源火箭仿真软件的设计与分析全指南 【免费下载链接】openrocket Model-rocketry aerodynamics and trajectory simulation software 项目地址: https://gitcode.com/GitHub_Trending/op/openrocket OpenRocket作为一款专业的开源火箭设计与仿真…...

告别纸上谈兵:在Multisim里调试交通灯仿真时,我踩过的这些坑希望你避开

告别纸上谈兵&#xff1a;在Multisim里调试交通灯仿真时&#xff0c;我踩过的这些坑希望你避开 第一次在Multisim里搭建完整的交通灯控制系统时&#xff0c;我以为只要按照教科书上的电路图连接好芯片和元件&#xff0c;仿真就能一帆风顺。但现实给了我当头一棒——数码管显示乱…...

有关数组的学习

数组的概念简介数组是编程中最基础也最常用的数据结构之一&#xff0c;理解它能帮你高效管理一组同类型的数据。1. 什么是数组&#xff1f;核心概念同类型&#xff1a;数组里的所有元素必须是相同的数据类型&#xff08;如全是 int 或全是 float&#xff09;。连续内存&#xf…...

7个革新性的REFramework应用技巧:游戏开发者的效率提升指南

7个革新性的REFramework应用技巧&#xff1a;游戏开发者的效率提升指南 【免费下载链接】REFramework REFramework 是 RE 引擎游戏的 mod 框架、脚本平台和工具集&#xff0c;能安装各类 mod&#xff0c;修复游戏崩溃、卡顿等问题&#xff0c;还有开发者工具&#xff0c;让游戏…...