JavaScript 迭代和访问项目的性能

JavaScript performance for iterating and accessing items

我有一个项目数组,有时需要迭代这些项目,有时需要直接访问其中的一个成员。所以我决定在两个变量中保留引用,一个数组和一个对象。我这样做:

const itemsArr = [];
const itemsObj = {};
const addItem = (data) => {
    const item = {
        id: data.id,
        name: data.name
    };
    itemsArr.push(item);
    itemsObj[data.id] = item;
}

const removeItem = (data) => {
    let i;
    for (i = 0; i < itemsArr.length; i++) {
        if (itemsArr[i].id === data.id) {
            itemsArr.splice(i, 1);
            break;
        }
    }
    itemsObj[data.id] = null;
    delete itemsObj[data.id];
}

const getWithId = (id) => {
    return itemsObj[id];
}

const getWithName = (name) => {
    let i;
    for (i = 0; i < itemsArr.length; i++) {
        if (itemsArr[i].name === name) {
            return itemsArr[i];
        }
    }
    return null
}

所以我管理两个对象并根据任务使用一个或另一个,我觉得这是最高效的方法,但也许有更好的方法,比如 Map 或 Set。

是否有一个 JavaScript 数据结构在迭代中优于数组和在查找中优于对象?

我认为 Object.keys 迭代一个对象有额外的性能成本,类似地 Array.filter 对数组的查找,所以我的直觉是使用数组进行迭代和对象进行查找,但是如果有是为两者优化的单一数据结构,我想知道并在我的代码中只使用一个 items

谢谢

首先,对于 lookup-by-id 部分,我建议使用 Map,而不是对象。对象针对常见的编程用例进行了优化,在这些用例中,它们的属性集随着时间的推移相当一致(值可能会改变,但对象的 shape [它具有的属性] 大多数情况下不会'吨)。 Map 作为通用 name/value 商店进行了优化,可以更好地处理该用例。更多内容在 on this MDN page.

Is there a single Javascript data structure that will outperform arrays in iteration and objects in lookup?

不,但是 Map 和数组之间的迭代速度差异在 广大 大多数应用程序中无关紧要。是的,在大多数情况下数组会更快(这在一定程度上取决于数组的构建方式),但这真的不太重要。

但是,如果您只通过 idname 访问,则您无论如何都不会进行迭代。您可能需要两个映射而不是一个映射和一个数组,因为无论哪种方式查找项目都是次线性的而不是线性的。它还使代码更简单:

const itemsByName = new Map();
const itemsById = new Map();
const addItem = (data) => {
    const item = {
        id: data.id,
        name: data.name
    };
    itemsByName.set(data.name, data);
    itemsByid.set(data.id, data);
};

const removeItem = (data) => {
    itemsByName.delete(data.name);
    itemsByid.delete(data.id);
};

const getWithId = (id) => {
    return itemsById.get(id);
};

const getWithName = (name) => {
    return itemsByName.get(name);
};

FWIW,如果上面没有 itemsByName 因为你不需要 name 查找是次线性的,下面是 getWithName 的代码使用 itemsById:

const getWithName = (name) => {
    for (const item of itemsById.values()) {
        if (data.name === name) {
            return data;
        }
    }
    return null;
};