У меня есть модель, возможно, с тысячами объектов. Мне было интересно, какой способ их хранения и извлечения одного объекта был бы наиболее эффективным, если у меня есть его идентификатор. Идентификаторы - длинные числа.
Вот 2 варианта, о которых я думал. в первом варианте это простой массив с возрастающим индексом. в варианте 2 это ассоциативный массив и, возможно, объект, если это имеет значение. Мой вопрос в том, какой из них более эффективен, когда мне в основном нужно получить один объект, но также иногда перебирать их и сортировать.
Вариант первый с неассоциативным массивом:
var a = [{id: 29938, name: 'name1'},
{id: 32994, name: 'name1'}];
function getObject(id) {
for (var i=0; i < a.length; i++) {
if (a[i].id == id)
return a[i];
}
}
Вариант второй с ассоциативным массивом:
var a = []; // maybe {} makes a difference?
a[29938] = {id: 29938, name: 'name1'};
a[32994] = {id: 32994, name: 'name1'};
function getObject(id) {
return a[id];
}
Обновить:
Хорошо, я понимаю, что использование массива во втором варианте исключено. Таким образом, в строке объявления должен быть второй вариант: var a = {};
и единственный вопрос: что лучше работает при извлечении объекта с заданным идентификатором: массив или объект, где идентификатор является ключом.
а также изменится ли ответ, если мне придется много раз сортировать список?
источник
Ответы:
Краткая версия: массивы в основном быстрее, чем объекты. Но 100% правильного решения не существует.
Обновление 2017 - Тест и результаты
var a1 = [{id: 29938, name: 'name1'}, {id: 32994, name: 'name1'}]; var a2 = []; a2[29938] = {id: 29938, name: 'name1'}; a2[32994] = {id: 32994, name: 'name1'}; var o = {}; o['29938'] = {id: 29938, name: 'name1'}; o['32994'] = {id: 32994, name: 'name1'}; for (var f = 0; f < 2000; f++) { var newNo = Math.floor(Math.random()*60000+10000); if (!o[newNo.toString()]) o[newNo.toString()] = {id: newNo, name: 'test'}; if (!a2[newNo]) a2[newNo] = {id: newNo, name: 'test' }; a1.push({id: newNo, name: 'test'}); }
Исходное сообщение - Объяснение
В вашем вопросе есть некоторые заблуждения.
В Javascript нет ассоциативных массивов. Только массивы и объекты.
Это массивы:
var a1 = [1, 2, 3]; var a2 = ["a", "b", "c"]; var a3 = []; a3[0] = "a"; a3[1] = "b"; a3[2] = "c";
Это тоже массив:
var a3 = []; a3[29938] = "a"; a3[32994] = "b";
По сути, это массив с дырками, потому что каждый массив имеет непрерывную индексацию. Это медленнее массивов без дыр. Но итерация вручную по массиву еще медленнее (в основном).
Это объект:
var a3 = {}; a3[29938] = "a"; a3[32994] = "b";
Вот тест производительности трех возможностей:
Поисковый массив против дырочного массива против теста производительности объекта
Отличное прочтение по этим темам в Smashing Magazine: Написание быстрого JavaScript с эффективным использованием памяти
источник
if (a1[i].id = id) result = a1[i];
Должно быть:if (a1[i].id === id) result = a1[i];
тест http://jsperf.com/array-vs-object-performance/37 исправляет этоЭто вообще не вопрос производительности, поскольку массивы и объекты работают по-разному (или, по крайней мере, должны). Массивы имеют непрерывный индекс
0..n
, а объекты сопоставляют произвольные ключи с произвольными значениями. Если вы хотите предоставить определенные ключи, единственный выбор - это объект. Если вас не интересуют ключи, то это массив.Если вы попытаетесь установить произвольные (числовые) ключи в массиве, вы действительно потеряете производительность , поскольку поведенчески массив заполнит все индексы между ними:
> foo = []; [] > foo[100] = 'a'; "a" > foo [undefined, undefined, undefined, ..., "a"]
(Обратите внимание, что на самом деле массив не содержит 99
undefined
значений, но он будет вести себя таким образом, поскольку в какой-то момент вы [должны] выполнять итерацию массива.)Литералы для обоих вариантов должны четко разъяснять, как их можно использовать:
var arr = ['foo', 'bar', 'baz']; // no keys, not even the option for it var obj = { foo : 'bar', baz : 42 }; // associative by its very nature
источник
user_id
« vs », имеющего ключи, поскольку,user_id
следовательно, к объекту пользователя можно получить доступ с использованиемuser_id
ключа»? Какой из них лучше по производительности? Любые предложения по этому поводу приветствуются :)С ES6 наиболее эффективным способом было бы использовать карту.
var myMap = new Map(); myMap.set(1, 'myVal'); myMap.set(2, { catName: 'Meow', age: 3 }); myMap.get(1); myMap.get(2);
Сегодня вы можете использовать функции ES6 с помощью прокладки ( https://github.com/es-shims/es6-shim ).
Производительность будет зависеть от браузера и сценария. Но вот один из
Map
наиболее эффективных примеров : https://jsperf.com/es6-map-vs-object-properties/2ССЫЛКА https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map
источник
efficiency
.В NodeJS, если вы знаете
ID
, цикл по массиву очень медленный по сравнению сobject[ID]
.const uniqueString = require('unique-string'); const obj = {}; const arr = []; var seeking; //create data for(var i=0;i<1000000;i++){ var getUnique = `${uniqueString()}`; if(i===888555) seeking = getUnique; arr.push(getUnique); obj[getUnique] = true; } //retrieve item from array console.time('arrTimer'); for(var x=0;x<arr.length;x++){ if(arr[x]===seeking){ console.log('Array result:'); console.timeEnd('arrTimer'); break; } } //retrieve item from object console.time('objTimer'); var hasKey = !!obj[seeking]; console.log('Object result:'); console.timeEnd('objTimer');
И результаты:
Array result: arrTimer: 12.857ms Object result: objTimer: 0.051ms
Даже если идентификатор поиска является первым в массиве / объекте:
Array result: arrTimer: 2.975ms Object result: objTimer: 0.068ms
источник
Я пытался буквально перенести это в новое измерение.
Учитывая двумерный массив, в котором оси x и y всегда имеют одинаковую длину, быстрее ли:
а) найдите ячейку, создав двумерный массив и просмотрев первый индекс, за которым следует второй индекс, то есть:
var arr=[][] var cell=[x][y]
или
б) создать объект со строковым представлением координат x и y, а затем выполнить один поиск по этому объекту, то есть:
var obj={} var cell = obj['x,y']
Результат.
Оказывается, намного быстрее выполнить два поиска по числовому индексу в массивах, чем один поиск свойства объекта.
Результаты здесь:
http://jsperf.com/arr-vs-obj-lookup-2
источник
Это зависит от использования. В случае, если поиск объектов выполняется очень быстро.
Вот пример Plunker для проверки производительности поиска массивов и объектов.
https://plnkr.co/edit/n2expPWVmsdR3zmXvX4C?p=preview
Вы увидите это; Поиск 5.000 элементов в коллекции массивов длиной 5.000 , взять
3000
миллисекундыОднако Глядя на 5000 элементов в объекте имеет 5.000 свойствами, принимают только
2
или3
miliseconsТакже создание дерева объектов не имеет большого значения
источник
У меня была аналогичная проблема, с которой я столкнулся, когда мне нужно хранить живые свечи из источника событий, ограниченного x элементами. Я мог бы сохранить их в объекте, где метка времени каждой свечи будет действовать как ключ, а сама свеча будет действовать как значение. Другая возможность заключалась в том, что я мог сохранить его в массиве, где каждый элемент был самой свечой. Одна из проблем живых свечей заключается в том, что они продолжают отправлять обновления с той же меткой времени, где последнее обновление содержит самые свежие данные, поэтому вы либо обновляете существующий элемент, либо добавляете новый. Итак, вот хороший тест, который пытается объединить все 3 возможности. Массивы в приведенном ниже решении в среднем как минимум в 4 раза быстрее. Не стесняйтесь играть
"use strict"; const EventEmitter = require("events"); let candleEmitter = new EventEmitter(); //Change this to set how fast the setInterval should run const frequency = 1; setInterval(() => { // Take the current timestamp and round it down to the nearest second let time = Math.floor(Date.now() / 1000) * 1000; let open = Math.random(); let high = Math.random(); let low = Math.random(); let close = Math.random(); let baseVolume = Math.random(); let quoteVolume = Math.random(); //Clear the console everytime before printing fresh values console.clear() candleEmitter.emit("candle", { symbol: "ABC:DEF", time: time, open: open, high: high, low: low, close: close, baseVolume: baseVolume, quoteVolume: quoteVolume }); }, frequency) // Test 1 would involve storing the candle in an object candleEmitter.on('candle', storeAsObject) // Test 2 would involve storing the candle in an array candleEmitter.on('candle', storeAsArray) //Container for the object version of candles let objectOhlc = {} //Container for the array version of candles let arrayOhlc = {} //Store a max 30 candles and delete older ones let limit = 30 function storeAsObject(candle) { //measure the start time in nanoseconds const hrtime1 = process.hrtime() const start = hrtime1[0] * 1e9 + hrtime1[1] const { symbol, time } = candle; // Create the object structure to store the current symbol if (typeof objectOhlc[symbol] === 'undefined') objectOhlc[symbol] = {} // The timestamp of the latest candle is used as key with the pair to store this symbol objectOhlc[symbol][time] = candle; // Remove entries if we exceed the limit const keys = Object.keys(objectOhlc[symbol]); if (keys.length > limit) { for (let i = 0; i < (keys.length - limit); i++) { delete objectOhlc[symbol][keys[i]]; } } //measure the end time in nano seocnds const hrtime2 = process.hrtime() const end = hrtime2[0] * 1e9 + hrtime2[1] console.log("Storing as objects", end - start, Object.keys(objectOhlc[symbol]).length) } function storeAsArray(candle) { //measure the start time in nanoseconds const hrtime1 = process.hrtime() const start = hrtime1[0] * 1e9 + hrtime1[1] const { symbol, time } = candle; if (typeof arrayOhlc[symbol] === 'undefined') arrayOhlc[symbol] = [] //Get the bunch of candles currently stored const candles = arrayOhlc[symbol]; //Get the last candle if available const lastCandle = candles[candles.length - 1] || {}; // Add a new entry for the newly arrived candle if it has a different timestamp from the latest one we storeds if (time !== lastCandle.time) { candles.push(candle); } //If our newly arrived candle has the same timestamp as the last stored candle, update the last stored candle else { candles[candles.length - 1] = candle } if (candles.length > limit) { candles.splice(0, candles.length - limit); } //measure the end time in nano seocnds const hrtime2 = process.hrtime() const end = hrtime2[0] * 1e9 + hrtime2[1] console.log("Storing as array", end - start, arrayOhlc[symbol].length) }
Вывод 10 - это предел здесь
Storing as objects 4183 nanoseconds 10 Storing as array 373 nanoseconds 10
источник
Если у вас есть отсортированный массив, вы можете выполнить двоичный поиск, и это намного быстрее, чем поиск объекта, вы можете увидеть мой ответ здесь:
Как быстрее искать в отсортированном массиве с помощью Javascript
источник
Индексированные поля (поля с цифровыми ключами) хранятся как священный массив внутри объекта. Следовательно, время поиска - O (1)
То же самое для массива поиска это O (1)
Перебор массива объектов и проверка их идентификаторов на соответствие предоставленному - это операция O (n).
источник