在JS中,如何保证数组插入时的唯一性?

在JS中,如何保证数组插入时的唯一性?
比如说:

let list = [{id:1},{id:2},{id:10}]

function insert(id) {
    if (!list.some(item => { return item.item == id })) {
        // 判断和插入不是原子性的,会不会有并发问题?
        list.push({id:id})
    }
}

这样插入的时候会不会产生并发问题?非前端出身,不是很懂JS

我能想到的解决方案

插入一个临时数组,然后有一个统一的死循环将临时数组的数据插入数组中

阅读 1.5k
avatarAI BotBETA

在JavaScript中,如果您正在处理单线程环境(如浏览器中的JavaScript或Node.js的默认模式),那么您提到的并发问题实际上是不存在的,因为JavaScript是单线程的。但是,如果您的代码在某种形式的多线程环境(如Web Workers或Node.js的Cluster模块)中运行,或者如果您的数组操作被异步事件(如定时器、网络请求回调等)打断,那么确实可能会出现问题。

对于单线程环境,您的代码中有一个小错误需要修正:在some方法的回调函数中,您应该直接比较item.idid,而不是item.item。下面是修正后的代码:

let list = [{id:1},{id:2},{id:10}];

function insert(id) {
    // 使用正确的属性名进行比较
    if (!list.some(item => item.id === id)) {
        list.push({id: id});
    }
}

对于多线程环境或异步操作,确实需要采取额外的措施来确保数组的唯一性。但是,在大多数情况下,JavaScript运行在单线程环境中,所以直接的比较和插入就足够了。

如果您确实担心在多线程环境中或者在异步操作中保持数据的一致性,您可能需要考虑使用其他机制,比如:

  1. 锁(Locks):在多线程环境中,可以使用锁来同步对共享资源的访问。但请注意,JavaScript的标准库中没有内置的锁机制,您可能需要使用第三方库或在Node.js中使用特定的模块(如async_hooksworker_threads)。
  2. 使用Set或其他数据结构:如果您只是需要存储唯一的标识符,而不是整个对象,那么使用Set可能是一个更简单的选择。但是,如果您需要存储对象并基于对象的某个属性来保持唯一性,那么您可能需要自己实现这种逻辑。
  3. 使用数据库:如果您的应用程序需要处理大量数据或需要更高级的数据一致性保证,那么使用数据库(如MongoDB、PostgreSQL等)可能是更好的选择。数据库通常提供了事务处理、锁和其他机制来确保数据的一致性和完整性。
  4. 原子操作:在某些情况下,您可能需要实现自定义的原子操作来确保多个操作要么全部成功,要么全部失败。这通常涉及到更复杂的编程模式和同步机制。

对于大多数前端和许多Node.js应用程序来说,简单的单线程检查和插入就足够了。如果您确实需要处理并发或异步情况,请根据您的具体需求和环境选择适当的解决方案。

6 个回答

不用考虑,js 是单线程的

不会,js是单线程的。
所以如果有重复操作会被你的 !list.some(item => { return item.item == id }) 拦截掉。

  1. JS不会有并发问题,JS只有一个主线程,并使用宏任务和微任务的形式抢占这个线程,所以不会存在两个线程同时操作一个变量的情况。
  2. 如果要判断数组内是否存在再加入数据,应该使用Set或Map对象,而不是Array。因为Array需要每次遍历,Set或Map可以直接判断是否重复,也可以无需判断直接添加(add方法已经判断了重复)。

你要改的话一开始就要把你的list换成map,代码例子:

const idDataMap = new Map([{id: 1}, {id: 2}, {id: 10}].map(e=>[e.id, e]))

// 然后添加的时候直接加就好了,不需要再判断了
// 其实并不需要写这个函数,没必要
function appendData(newItem){
    idDataMap.set(newItem.id, newItem);
}

因为Map的key会储存为hash,后续会根据特征直接取数据,不需要遍历。Map在常用的数据结构里是性能最好的,简单来看,不管多大的数据取出来复杂度都是O(1),也就是一次操作就可以找到目标,不管数据多大。不过为了生成Map肯定在最开始是要遍历一次的,这一次遍历后,后续就随便用不需要担心性能了。

使用 Set:

let list = [{id: 1}, {id: 2}, {id: 10}];
let idSet = new Set(list.map(item => item.id));

function insert(id) {
    if (!idSet.has(id)) {
        list.push({id: id});
        idSet.add(id);
    }
}

使用 Map:

let list = [{id: 1}, {id: 2}, {id: 10}];
let idMap = new Map(list.map(item => [item.id, item]));

function insert(id) {
    if (!idMap.has(id)) {
        let newItem = {id: id};
        list.push(newItem);
        idMap.set(id, newItem);
    }
}

这两种方法都确保了插入时的唯一性,并避免了并发问题。

JS没有线程安全问题

JS 是单线程模型,不存在并发问题。
若你需要一个保持唯一性的数据结构,除了上面伙伴的做法,不妨试试笔者的 refresh-set 以及对应
npm package

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏