做后台开发时,经常要处理成千上万甚至上亿条数据。比如用户行为日志、订单流水、传感器采集记录,这些场景下数组不再是简单的几个元素,而是一动就是几十万起步。这时候如果还用常规方式遍历、过滤、去重,程序跑得比蜗牛还慢,内存直接爆掉也不奇怪。
别急着循环,先想想数据结构
很多人一上来就 for 循环,不管三七二十一先把数组扫一遍。但面对百万级数组,这种做法代价太大。举个例子:你要从一千万条用户 ID 中找出重复出现的,用双重 for 循环等于执行一百万亿次比较,别说运行了,光听着就头皮发麻。
换个思路:用哈希表(JavaScript 里的 Map 或 Object)来记录每个 ID 出现的次数。一次遍历就够了,时间复杂度从 O(n²) 降到 O(n)。实际测试中,处理 100 万条数据,for 嵌套可能要几分钟,而哈希表方案不到一秒。
const counts = new Map();
for (const id of largeArray) {
counts.set(id, (counts.get(id) || 0) + 1);
}
// 找出重复项
const duplicates = [];
for (const [id, count] of counts) {
if (count > 1) {
duplicates.push(id);
}
}
分块处理,别让浏览器卡死
前端也常遇到大数据量数组,比如导出百万条报表预览。一次性渲染到页面,浏览器直接无响应。这时候得用“分块”策略,把大数组拆成小段,用 requestIdleCallback 或 setTimeout 分批处理。
就像吃饭一样,没人会一口吞下一整碗米饭。每次处理 5000 条,留点时间给主线程喘口气,页面依然能点击、滚动,用户体验就好多了。
function processInChunks(array, callback, chunkSize = 5000) {
let index = 0;
function processChunk() {
const end = Math.min(index + chunkSize, array.length);
for (; index < end; index++) {
callback(array[index]);
}
if (index < array.length) {
setTimeout(processChunk, 0); // 放入事件队列
}
}
processChunk();
}
善用生成器,节省内存开销
有时候你并不需要立刻拿到全部结果。比如从一个超大数组里筛选满足条件的数据,后续是逐条使用的。这时候用生成器函数 yield 逐个返回,比先生成完整新数组再使用要省得多。
想象你在查图书馆的书目,系统不是把所有匹配结果一次性打印出来给你,而是让你一页一页翻,每页只加载当前需要的。这就是惰性求值的优势。
function* filterLargeArray(largeArray, predicate) {
for (const item of largeArray) {
if (predicate(item)) {
yield item;
}
}
}
// 使用时才计算
const bigData = [...]; // 超大数组
const activeItems = filterLargeArray(bigData, item => item.status === 'active');
for (const item of activeItems) {
console.log(item); // 按需输出
}
后端别忘了流式读取
在 Node.js 或 Java 这类环境里,处理大数组往往意味着读大文件。与其一次性读进内存,不如用流(stream)边读边处理。比如读一个 2GB 的 CSV 文件,用 fs.createReadStream 一行行解析,配合管道操作,内存占用始终稳定在几十 MB。
这就像自来水,你不把整条河引进家里,而是需要时打开水龙头接一点。数据流也是这个道理,按需流动,不囤积。