You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

数组元素删除:splice与filter实现对比及更优方案咨询

两种数组元素删除实现的优劣对比及更优方案

嘿,这个问题问得很到位,咱们来仔细拆解这两种实现的差异,再聊聊不同场景下的更优方案~

先看两种实现的核心逻辑

你的splice版本

public static deleteElementFromArray<T>(list: T[], element: T): T[] {
  if (!list) {
    return [];
  }
  const index = list.indexOf(element);
  if (index > -1) {
    list.splice(index, 1);
  }
  return list;
}

这个方法的核心是原地修改原数组:通过indexOf找到第一个匹配元素的索引,再用splice直接从原数组中移除该元素,最后返回原数组引用。

朋友的filter版本

users = users.filter(u => u !== toto);

这个方法的核心是返回新数组、原数组不变:遍历整个数组,保留所有不等于目标元素的项,最终返回一个全新的数组。


两种实现的优劣对比

你的splice实现

优势

  • 内存开销小:直接修改原数组,不需要额外内存创建新数组,适合处理超大数组且内存敏感的场景
  • 精准控制删除数量:只删除第一个匹配的元素,如果你的业务逻辑明确只需要移除第一个匹配项,这个逻辑更贴合需求
  • 提前终止遍历:indexOf找到匹配元素后就会停止遍历,不需要像filter那样走完整个数组,在只删第一个的场景下性能略优

劣势

  • 副作用风险:原地修改原数组可能导致其他依赖该数组的代码意外触发变更,比如在React/Vue等框架中,原地修改数组不会触发视图更新(框架依赖引用变化)
  • 返回值易混淆:返回的是原数组的引用,调用方可能误以为拿到了新数组,进而引发逻辑错误
  • 无法批量删除:如果数组中有多个相同的目标元素,只会删除第一个,无法一次性移除所有匹配项

朋友的filter实现

优势

  • 纯函数无副作用:不修改原数组,完全符合函数式编程风格,在现代前端框架中更新状态时更安全(能正确触发视图重渲染)
  • 批量删除更简洁:自动移除所有匹配的元素,如果业务需求是删除所有目标项,这个写法比循环调用splice简洁太多
  • 返回值明确:返回全新数组,调用方可以清晰知道这是一个新的引用,不会和原数组混淆

劣势

  • 内存开销更大:需要创建新数组存储结果,对于超大数组来说,会额外占用一倍左右的内存
  • 遍历无终止:无论是否找到匹配元素,都会遍历整个数组,在只需要删除第一个匹配项的场景下做了无用功,性能略逊于splice
  • 逻辑不符合单一删除需求:如果只需要删除第一个匹配项,这个方法会额外过滤掉后续所有可能的匹配项,不符合预期

不同场景下的更优解决方案

场景1:需要删除第一个匹配项,且避免修改原数组

如果不想触发副作用,又只需要删第一个元素,可以用slice拼接新数组:

public static deleteFirstElementFromArray<T>(list: T[], element: T): T[] {
  if (!list) return [];
  const index = list.indexOf(element);
  // 没找到匹配项就返回原数组的拷贝,避免修改原数组
  if (index === -1) return [...list];
  // 拼接索引前后的元素,生成新数组
  return [...list.slice(0, index), ...list.slice(index + 1)];
}

这个方法既保留了“只删第一个”的精准性,又做到了纯函数无副作用。

场景2:需要删除所有匹配项,且追求更灵活的逻辑

如果需要自定义匹配规则(比如匹配对象的某个属性),filter依然是最优选择,比如删除所有id为1的用户:

users = users.filter(u => u.id !== 1);

如果追求和filter性能相当但写法不同的方式,也可以用reduce:

public static deleteAllElementsFromArray<T>(list: T[], element: T): T[] {
  if (!list) return [];
  return list.reduce((acc, curr) => {
    if (curr !== element) acc.push(curr);
    return acc;
  }, [] as T[]);
}

场景3:处理引用类型元素(比如对象)

上面的方法都是基于浅比较(===),如果元素是对象(比如{id: 1, name: 'toto'}),浅比较无法匹配不同引用的同内容对象。这时候需要用findIndex(删第一个)或filter(删所有)结合自定义比较逻辑:

// 删除第一个id匹配的对象
public static deleteFirstObjectFromArray<T extends {id: number}>(list: T[], targetId: number): T[] {
  if (!list) return [];
  const index = list.findIndex(item => item.id === targetId);
  if (index === -1) return [...list];
  return [...list.slice(0, index), ...list.slice(index + 1)];
}

// 删除所有id匹配的对象
users = users.filter(u => u.id !== targetId);

场景4:追求极致性能,且允许修改原数组

如果你的业务场景允许修改原数组,且只需要删除第一个匹配项,你的splice方法已经很高效了,只需优化一下边界判断即可:

public static deleteElementFromArray<T>(list: T[], element: T): T[] {
  if (!list || list.length === 0) {
    return [];
  }
  const index = list.indexOf(element);
  if (index > -1) {
    list.splice(index, 1);
  }
  return list;
}

总结

选择哪种方法完全取决于你的业务场景:

  • 若需要原地修改、只删第一个、内存敏感:用你的splice实现
  • 若需要纯函数、删所有匹配项、框架状态更新:用filter实现
  • 若需要纯函数、只删第一个:用slice拼接的方案
  • 若处理引用类型元素:结合findIndex/filter和自定义比较逻辑

内容的提问来源于stack exchange,提问作者dev34fr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:10:20