用Set记录已加载项索引是虚拟滚动中高效去重方案,因其O(1)查询性能及唯一性保障,需持久化存储、适时清理,并严格对齐数据源逻辑索引。

在长列表虚拟滚动中,用 Set 记录已加载项的索引(index)是一种轻量、高效的去重方案。关键不是“能不能用 Set”,而是“怎么用得对、不漏、不误判”。核心思路是:把已成功渲染或已触发数据加载的项索引存入 Set,滚动时查它来跳过重复请求或重复渲染逻辑。
为什么用 Set 而不是数组或 Map?
Set 天然适合存储唯一索引值,且 .has(index) 是 O(1) 时间复杂度,比数组 .includes()(O(n))快得多;相比 Map,它更简洁——你不需要键值对,只要“这个 index 加载过了”这个布尔事实。
典型使用位置:配合滚动监听或 render 函数
虚拟滚动通常只渲染可视区域(如 viewport 上下各缓冲 5 项)的元素。你应在以下时机更新和查询 Set:
将后端 Java 接口文档转换为前端 TypeScript 类型定义(interface / type)。输入:飞书文档、Spring Boot 接口文档、Swagger/OpenAPI、手写 API 文档等。输出:包含请求参数与响应结果的 .md 文件,含完整 JSDoc 注释。触发关键词:Java 转 TS、接口转换、生成前端类型、接口文档转 TypeScript、DTO/VO/BO 转 interface、后端文档转前端类型、生成接口类型、类型定义生成。
- 在
renderItem或useEffect(React)中,当某项首次进入可视区且尚未加载数据时,发起请求,并成功后loadedIndexes.add(index) - 在滚动计算可见区间(
startIdx~endIdx)后,遍历该区间,用loadedIndexes.has(i)判断是否跳过 loading 状态或二次请求 - 避免在每次 render 都新建
Set——它必须是持久的,比如放在组件外层、ref、useState或自定义 Hook 的闭包中
注意边界与清理场景
单纯靠 Set 不解决内存无限增长问题。需结合业务策略清理:
- 若列表极长(百万级),可限制
Set最大尺寸,用 LRU 思路:只保留最近 N 个加载过的 index(需自行实现或用Map记时间戳) - 用户搜索/筛选导致列表重置时,应调用
loadedIndexes.clear() - 滚动到顶部重新加载全量数据时,也建议清空再重建
- 注意 index 是逻辑索引(如数组下标),不是 DOM 序号或 key —— 确保它和你的数据源严格对齐
一个最小可用示例(伪代码)
(以 React + 自定义 Hook 为例)
const loadedIndexes = useRef(new Set());
// 滚动后计算 visibleRange: [start, end]
visibleRange.forEach(idx => {
if (!loadedIndexes.current.has(idx)) {
fetchItem(idx).then(() => {
loadedIndexes.current.add(idx);
forceUpdate(); // 或更新局部状态
});
}
});
// 清理入口(如切换 tab)
const resetLoadingState = () => loadedIndexes.current.clear();
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










