Array vs. Efisiensi objek dalam JavaScript

145

Saya punya model dengan kemungkinan ribuan objek. Saya bertanya-tanya apa yang akan menjadi cara paling efisien untuk menyimpannya dan mengambil satu objek setelah saya memilikinya id. Nomornya panjang.

Jadi ini adalah 2 opsi yang saya pikirkan. dalam opsi satu itu adalah array sederhana dengan indeks yang bertambah. dalam opsi 2 ini adalah array asosiatif dan mungkin sebuah objek, jika itu membuat perbedaan. Pertanyaan saya adalah mana yang lebih efisien, ketika saya sebagian besar perlu mengambil satu objek, tetapi juga kadang-kadang loop melalui mereka dan urutkan.

Opsi satu dengan array non asosiatif:

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];
    }
}

Opsi dua dengan array asosiatif:

var a = [];  // maybe {} makes a difference?
a[29938] = {id: 29938, name: 'name1'};
a[32994] = {id: 32994, name: 'name1'};
function getObject(id) {
    return a[id];
}

Memperbarui:

OK, saya mendapatkan bahwa menggunakan array pada opsi kedua adalah keluar dari pertanyaan. Jadi baris deklarasi pilihan kedua harus benar-benar: var a = {};dan satu-satunya pertanyaan adalah: apa yang berkinerja lebih baik dalam mengambil objek dengan id yang diberikan: array atau objek di mana id adalah kuncinya.

dan juga, akankah jawabannya berubah jika saya harus mengurutkan daftar berkali-kali?

Moshe Shaham
sumber
1
ini membantu mungkin :: stackoverflow.com/questions/13309464/…
Sudhir Bastakoti
Apakah Anda memerlukan koleksi yang diurutkan setiap saat? Jika demikian, tidak ada pilihan selain array (walaupun tidak menggunakan indeks seperti yang Anda lakukan saat ini).
Jon
@Jon sebenarnya, saya lakukan. apa yang kamu maksud dengan "seperti yang kamu lakukan saat ini"?
Moshe Shaham
1
@ MosheShaham: Array (harus) memiliki indeks kontinu mulai dari 0. Jika Anda menggunakan array, jangan lakukan hal lain.
Jon
Saya kira tolok ukur ini akan menjawab bagian pertama dari pertanyaan Anda: jsben.ch/#/Y9jDP
EscapeNetscape

Jawaban:

143

Versi singkat: Array sebagian besar lebih cepat dari objek. Tetapi tidak ada solusi yang 100% benar.

Perbarui 2017 - Tes dan Hasil

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'});
}

pengaturan tes hasil tes

Posting Asli - Penjelasan

Ada beberapa kesalahpahaman dalam pertanyaan Anda.

Tidak ada array asosiatif di Javascript. Hanya Array dan Objek.

Ini adalah array:

var a1 = [1, 2, 3];
var a2 = ["a", "b", "c"];
var a3 = [];
a3[0] = "a";
a3[1] = "b";
a3[2] = "c";

Ini juga sebuah array:

var a3 = [];
a3[29938] = "a";
a3[32994] = "b";

Ini pada dasarnya adalah array dengan lubang di dalamnya, karena setiap array memang memiliki pengindeksan terus-menerus. Ini lebih lambat daripada array tanpa lubang. Tetapi iterasi secara manual melalui array bahkan lebih lambat (kebanyakan).

Ini adalah objek:

var a3 = {};
a3[29938] = "a";
a3[32994] = "b";

Berikut ini adalah tes kinerja dari tiga kemungkinan:

Lookup Array vs Holey Array vs Object Performance Test

Bacaan luar biasa tentang topik-topik ini di Smashing Magazine: Menulis JavaScript cepat dan efisien memori

Puncak gunung
sumber
1
@ Moshe Dan dengan demikian semua diskusi tentang kinerja dalam Javascript harus dilakukan. : P
deceze
9
Ini sangat tergantung pada data dan ukuran data yang Anda kerjakan. Kumpulan data yang sangat kecil dan objek kecil akan berkinerja lebih baik dengan array. Jika Anda berbicara tentang pencarian dalam kumpulan data besar di mana Anda menggunakan objek sebagai peta maka objek lebih efisien. jsperf.com/array-vs-object-performance/35
f1v
5
Setuju dengan f1v, tetapi Revisi 35 memiliki kekurangan dalam tes: if (a1[i].id = id) result = a1[i]; Seharusnya: if (a1[i].id === id) result = a1[i];Uji http://jsperf.com/array-vs-object-performance/37 mengoreksi itu
Charles Byrne
1
Lihat http://jsperf.com/array-vs-object-performance/71 . Memiliki subset data yang lebih kecil (saya seharusnya di-looped untuk pembuatan data, tapi saya ingin hole dalam array) sekitar 93 objek dibandingkan dengan 5000. Loop luar adalah Id untuk mencari yang tersebar di dalam array objek (mulai tengah dan akhir) dan Saya menambahkan id yang hilang juga sehingga pencarian Array harus melintasi semua elemen. Array berlubang, Objek dengan Kunci, lalu Array Manual. Jadi seperti yang dinyatakan f1v itu benar-benar tergantung pada ukuran data dan di mana data untuk pencarian array manual.
Charles Byrne
4
Jawaban ini dapat ditingkatkan dengan merangkum kesimpulan jsPerf di sini di posting ini - terutama karena hasil jsPerf adalah jawaban nyata untuk pertanyaan tersebut. Sisanya ekstra. Ini lebih relevan di saat-saat jsPerf sedang down (seperti sekarang). meta.stackexchange.com/questions/8231/…
Jeff
23

Sama sekali bukan pertanyaan kinerja, karena array dan objek bekerja sangat berbeda (atau setidaknya seharusnya). Array memiliki indeks kontinu 0..n, sementara objek memetakan kunci arbitrer ke nilai arbitrer. Jika Anda ingin menyediakan kunci tertentu, satu-satunya pilihan adalah objek. Jika Anda tidak peduli dengan kunci, array itu.

Jika Anda mencoba mengatur kunci acak (numerik) pada array, Anda benar-benar mengalami kerugian kinerja , karena biasanya array akan mengisi semua indeks di antara:

> foo = [];
  []
> foo[100] = 'a';
  "a"
> foo
  [undefined, undefined, undefined, ..., "a"]

(Perhatikan bahwa array sebenarnya tidak mengandung 99 undefinednilai, tetapi akan berperilaku seperti ini karena Anda [seharusnya] mengulangi array di beberapa titik.)

Literal untuk kedua opsi harus membuatnya sangat jelas bagaimana mereka dapat digunakan:

var arr = ['foo', 'bar', 'baz'];     // no keys, not even the option for it
var obj = { foo : 'bar', baz : 42 }; // associative by its very nature
tipuan
sumber
Saya tidak ingin menyediakan kunci khusus. Saya ingin tahu apa yang berkinerja lebih baik, dan saya akan bekerja dengan itu. OK, jadi pada opsi kedua array keluar dari pertanyaan. tapi bagaimana dengan objek vs array non asosiatif?
Moshe Shaham
1
@ Moshe Tidak ada yang namanya array non-asosiatif dalam Javascript. Jika Anda membutuhkan kunci (angka atau string), gunakan objek. Jika Anda hanya perlu daftar (dipesan), gunakan array. Titik. Kinerja tidak masuk dalam diskusi. Jika kinerja sangat penting dan Anda dapat hidup dengan kunci Anda, cobalah yang mana yang lebih baik untuk Anda.
deceze
5
Tapi saya ingin tahu apa yang berkinerja lebih baik: mengambil objek dari array (dengan mengulanginya) atau dari objek "asosiatif" di mana id adalah kuncinya. Maaf jika pertanyaan saya tidak jelas ...
Moshe Shaham
2
@ Moshe Jika Anda mengakses sesuatu dengan kunci, baik dalam objek atau array, itu akan selalu jauh lebih cepat daripada perulangan melalui wadah mencoba menemukan apa yang Anda inginkan. Perbedaan mengakses item dengan kunci dalam array atau objek mungkin diabaikan. Looping jelas lebih buruk.
deceze
1
@deceze - Bagaimana "tentang array yang menahan objek pengguna dan untuk mendapatkan objek pengguna, diperlukan loop untuk mendapatkan objek pengguna berdasarkan user_id" vs "objek yang memiliki kunci karena dengan user_iddemikian objek pengguna dapat diakses menggunakan user_idkunci sebagai"? Mana yang lebih baik dalam hal kinerja? Setiap saran tentang hal ini dihargai :)
Rayon
13

Dengan ES6 cara paling performan adalah menggunakan Peta.

var myMap = new Map();

myMap.set(1, 'myVal');
myMap.set(2, { catName: 'Meow', age: 3 });

myMap.get(1);
myMap.get(2);

Anda dapat menggunakan fitur ES6 hari ini menggunakan shim ( https://github.com/es-shims/es6-shim ).

Kinerja akan bervariasi tergantung pada browser dan skenario. Tapi di sini adalah satu contoh di mana Mapyang paling performant: https://jsperf.com/es6-map-vs-object-properties/2


REFERENSI https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map

sandstrom
sumber
11
Punya sumber daya untuk mendukung ini? Dari pengamatan saya sejauh ini ES6 Sets lebih cepat dari array tetapi ES6 Maps lebih lambat dari kedua objek dan array
Steel Brain
1
Ini lebih "semantik", bukan lebih banyak pemain, yang merupakan pertanyaan.
AlexG
3
@AlexG cukup yakin judulnya menyatakan dengan jelas efficiency.
Qix - MONICA DISEBUTKAN
@Qix Ya, salah saya: o
AlexG
8

Dalam NodeJS jika Anda tahu ID, perulangan melalui array sangat lambat dibandingkan dengan 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');

Dan hasilnya:

Array result:
arrTimer: 12.857ms
Object result:
objTimer: 0.051ms

Bahkan jika ID pencarian adalah yang pertama dalam array / objek:

Array result:
arrTimer: 2.975ms
Object result:
objTimer: 0.068ms
Paweł
sumber
5

Saya mencoba untuk membawa ini ke dimensi berikutnya, secara harfiah.

Diberikan array 2 dimensi, di mana sumbu x dan y selalu sama panjang, apakah lebih cepat untuk:

a) mencari sel dengan membuat array dua dimensi dan mencari indeks pertama, diikuti oleh indeks kedua, yaitu:

var arr=[][]    
var cell=[x][y]    

atau

b) membuat objek dengan representasi string dari koordinat x dan y, dan kemudian melakukan pencarian tunggal pada objek tersebut, yaitu:

var obj={}    
var cell = obj['x,y']    

Hasil:
Ternyata lebih cepat melakukan dua pencarian indeks numerik pada array, daripada satu pencarian properti pada objek.

Hasil di sini:

http://jsperf.com/arr-vs-obj-lookup-2

Davem M
sumber
3

Itu tergantung penggunaan. Jika kasing objek adalah sangat cepat.

Berikut ini adalah contoh Plunker untuk menguji kinerja pencarian array dan objek.

https://plnkr.co/edit/n2expPWVmsdR3zmXvX4C?p=preview

Anda akan melihat itu; Mencari 5.000 item dalam 5.000 koleksi array panjang, ambil alih 3000milidetik

Namun Mencari 5.000 item di objek memiliki 5.000 properti, ambil saja2 atau3 milidetik

Juga membuat pohon objek tidak membuat perbedaan besar

Mehmet Otkun
sumber
0

Saya memiliki masalah serupa yang saya hadapi di mana saya perlu menyimpan lilin langsung dari sumber acara terbatas pada x item. Saya bisa menyimpannya di objek di mana cap waktu setiap lilin akan bertindak sebagai kunci dan lilin itu sendiri akan bertindak sebagai nilainya. Kemungkinan lain adalah bahwa saya bisa menyimpannya di sebuah array di mana setiap item adalah lilin itu sendiri. Satu masalah tentang lilin langsung adalah bahwa mereka terus mengirim pembaruan pada stempel waktu yang sama di mana pembaruan terbaru menyimpan data terbaru sehingga Anda memperbarui item yang ada atau menambah yang baru. Jadi di sini adalah tolok ukur yang bagus yang mencoba untuk menggabungkan semua 3 kemungkinan. Array dalam solusi di bawah ini minimal 4x lebih cepat rata-rata. Jangan ragu untuk bermain

"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)
}

Kesimpulan 10 adalah batasnya di sini

Storing as objects 4183 nanoseconds 10
Storing as array 373 nanoseconds 10
PirateApp
sumber