जावास्क्रिप्ट में ऐरे बनाम वस्तु दक्षता


145

मेरे पास संभवतः हजारों वस्तुओं वाला एक मॉडल है। मैं सोच रहा था कि मेरे पास एक आईडी होने के बाद उन्हें संग्रहीत करने और किसी एक ऑब्जेक्ट को प्राप्त करने का सबसे कुशल तरीका क्या होगा। आईडी की संख्या लंबी होती है।

तो ये 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 = {};और एकमात्र सवाल यह है: किसी दिए गए आईडी के साथ किसी वस्तु को प्राप्त करने में बेहतर प्रदर्शन क्या है: एक सरणी या एक वस्तु जहां आईडी कुंजी है।

और भी, क्या उत्तर बदल जाएगा यदि मुझे सूची को कई बार क्रमबद्ध करना होगा?


1
यह मदद की जा सकती है :: stackoverflow.com/questions/13309464/…
सुधीर बस्ताकोटी

क्या आपको हर समय छांटे गए संग्रह की आवश्यकता है? यदि ऐसा है, तो सरणी के अलावा कोई अन्य विकल्प नहीं है (हालांकि वर्तमान में आप जैसे अनुक्रमित का उपयोग नहीं कर रहे हैं)।
जॉन

@ वास्तव में, मैं करता हूँ। "जैसे आप वर्तमान में करते हैं" से आपका क्या मतलब है?
मोशे शाहम

1
@MosheShaham: Arrays (चाहिए) में 0. से शुरू होने वाले लगातार अनुक्रमित हैं। यदि आप सरणियों का उपयोग करते हैं, तो कुछ और न करें।
जॉन

मुझे लगता है कि यह बेंचमार्क आपके प्रश्न के पहले भाग का जवाब देगा: jsben.ch/#/Y9jDP
EscapeNetscape

जवाबों:


143

संक्षिप्त संस्करण: एरे ज्यादातर वस्तुओं की तुलना में तेज़ होते हैं। लेकिन 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'});
}

परीक्षण व्यवस्था परीक्षण के परिणाम

मूल पोस्ट - स्पष्टीकरण

आपके प्रश्न में कुछ गलत धारणाएँ हैं।

जावास्क्रिप्ट में कोई सहयोगी सरणियाँ नहीं हैं। केवल एरे और ऑब्जेक्ट्स।

ये सरणियाँ हैं:

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";

यहाँ तीन संभावनाओं का प्रदर्शन परीक्षण किया गया है:

लुकअप ऐरे बनाम होले एरे बनाम ऑब्जेक्ट प्रदर्शन टेस्ट

स्मैशिंग मैगज़ीन में इन विषयों के बारे में एक उत्कृष्ट रीडिंग: तेज मेमोरी कुशल जावास्क्रिप्ट लिखना


1
@Moshe और इसलिए जावास्क्रिप्ट में प्रदर्शन के बारे में सभी चर्चा की जानी चाहिए। : P
छल

9
यह वास्तव में आपके द्वारा काम कर रहे डेटा के डेटा और आकार पर निर्भर करता है। बहुत छोटे डेटा सेट और छोटे ऑब्जेक्ट सरणियों के साथ बहुत बेहतर प्रदर्शन करेंगे। यदि आपकी लुकअप के बारे में एक बड़े डेटा सेट में बात हो रही है जहाँ आप किसी ऑब्जेक्ट को मैप के रूप में उपयोग करते हैं तो ऑब्जेक्ट अधिक कुशल है। jsperf.com/array-vs-object-performance/35
f1v

5
F1v से सहमत हैं, लेकिन संशोधन 35 का परीक्षण में दोष है: if (a1[i].id = id) result = a1[i];होना चाहिए: if (a1[i].id === id) result = a1[i];टेस्ट http://jsperf.com/array-vs-object-performance/37 सही है कि
चार्ल्स बर्न

1
Http://jsperf.com/array-vs-object-performance/71 देखें । डेटा का एक छोटा उपसमूह है (मुझे डेटा निर्माण के लिए लूप किया जाना चाहिए था, लेकिन 5000 की तुलना में लगभग 93 ऑब्जेक्ट्स में मुझे सरणी में छेद चाहिए)। बाहरी लूप Ids हैं जो ऑब्जेक्ट एरे में बिखरे हुए हैं (मध्य और अंत में शुरुआत) मैंने एक लापता आईडी जोड़ी और साथ ही एरियर लुकअप को सभी तत्वों को पार करना होगा। होल एरे, ऑब्जेक्ट बाय की, फिर मैनुअल एरे। तो जैसा कि f1v ने कहा है कि यह वास्तव में डेटा के आकार पर निर्भर करता है और जहां डेटा मैन्युअल ऐरे खोजों के लिए है।
चार्ल्स बर्न

4
इस उत्तर को यहाँ इस पोस्ट में jsPerf निष्कर्षों के सारांश द्वारा सुधारा जा सकता है - विशेषकर क्योंकि jsPerf परिणाम प्रश्न का वास्तविक उत्तर है। बाकी अतिरिक्त है। यह ऐसे समय में अधिक प्रासंगिक है जब jsPerf नीचे हो (जैसे अभी)। meta.stackexchange.com/questions/8231/…
जेफ

23

यह वास्तव में एक प्रदर्शन सवाल नहीं है, क्योंकि सरणियाँ और ऑब्जेक्ट बहुत अलग तरीके से काम करते हैं (या कम से कम माना जाता है)। Arrays में एक सतत सूचकांक होता है 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

मैं विशिष्ट कुंजियों की आपूर्ति नहीं करना चाहता। मैं जानना चाहता हूं कि बेहतर प्रदर्शन क्या है, और मैं इसके साथ काम करूंगा। ठीक है, इसलिए दूसरे विकल्प में एक सरणी प्रश्न से बाहर है। लेकिन एक वस्तु बनाम गैर साहचर्य सरणी के बारे में क्या?
मोशे शाहम

1
@Moshe जावास्क्रिप्ट में एक गैर-सहयोगी सरणी के रूप में ऐसी कोई बात नहीं है। यदि आपको कुंजी (संख्या या तार) की आवश्यकता है, तो किसी ऑब्जेक्ट का उपयोग करें। यदि आपको केवल (आदेशित) सूची चाहिए, तो सरणियों का उपयोग करें। अवधि। प्रदर्शन चर्चा में प्रवेश नहीं करता है। यदि प्रदर्शन महत्वपूर्ण है और आप अपनी कुंजी के साथ किसी भी तरह से रह सकते हैं, तो कोशिश करें कि कौन सा आपके लिए बेहतर है।
deceze

5
लेकिन मैं जानना चाहता हूं कि बेहतर प्रदर्शन क्या है: एक सरणी से एक वस्तु को पुनर्प्राप्त करना (इसके माध्यम से लूप करके) या एक "साहचर्य" ऑब्जेक्ट से जहां आईडी कुंजी है। मुझे क्षमा करें यदि मेरा प्रश्न स्पष्ट नहीं था ...
मोशे शाहम

2
@Moshe यदि आप किसी भी चीज को कुंजी या एरे से एक्सेस करते हैं, तो हमेशा कंटेनर में लूपिंग की तुलना में अनन्त रूप से तेज होना चाहिए जो आप चाहते हैं। किसी सरणी या किसी वस्तु में कुंजी द्वारा किसी वस्तु तक पहुँचने का अंतर नगण्य है। लूपिंग स्पष्ट रूप से या तो बदतर है।
deceze

1
@deceze - उपयोगकर्ता वस्तुओं को रखने के लिए और उपयोगकर्ता की वस्तु प्राप्त करने के बारे में कैसे ", user_id" बनाम "ऑब्जेक्ट के आधार पर उपयोगकर्ता ऑब्जेक्ट प्राप्त करने के लिए एक लूप की आवश्यकता होती है, user_idइसलिए उपयोगकर्ता ऑब्जेक्ट user_idको कुंजी के रूप में उपयोग किया जा सकता है "? प्रदर्शन के मामले में कौन सा बेहतर है? इस पर किसी भी सुझाव की सराहना की जाती है :)
रेयान

13

ES6 के साथ सबसे अच्छा तरीका एक मानचित्र का उपयोग करना होगा।

var myMap = new Map();

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

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

आप एक शिम ( https://github.com/es-shims/es6-shim ) का उपयोग करके आज ES6 सुविधाओं का उपयोग कर सकते हैं ।

ब्राउज़र और परिदृश्य के आधार पर प्रदर्शन अलग-अलग होंगे। लेकिन यहां एक उदाहरण है जहां Mapसबसे अधिक प्रदर्शन किया जाता है: https://jsperf.com/es6-map-vs-object-proties/2


संदर्भ https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map


11
इसे वापस करने के लिए कोई संसाधन मिला? मेरी टिप्पणियों से अब तक ES6 सेट सरणियों की तुलना में तेज़ हैं, लेकिन ES6 मैप्स ऑब्जेक्ट्स और एरेज़ दोनों की तुलना में धीमी हैं
स्टील ब्रेन

1
यह अधिक "अर्थ" है, अधिक प्रदर्शन करने वाला नहीं है, जो प्रश्न था।
एलेक्स

3
@AlexG को यकीन है कि शीर्षक स्पष्ट रूप से बताता है efficiency
Qix - MONICA WAS ने

@Qix हाँ, मेरा बुरा: ओ
एलेक्सजॉव

8

में 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

5

मैंने इसे अगले आयाम पर ले जाने की कोशिश की, शाब्दिक रूप से।

एक 2 आयामी सरणी को देखते हुए, जिसमें x और y अक्ष हमेशा एक ही लंबाई के होते हैं, क्या यह तेजी से होता है:

क) एक दो आयामी सरणी बनाकर और पहले सूचकांक को देखने के बाद सेल को देखें, इसके बाद दूसरा सूचकांक, यानी:

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

या

b) x और y निर्देशांक के एक स्ट्रिंग प्रतिनिधित्व के साथ एक वस्तु बनाते हैं, और फिर उस obj पर एक ही खोज करते हैं, अर्थात:

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

परिणाम:
पता चलता है कि यह ऑब्जेक्ट पर एक संपत्ति की खोज की तुलना में सरणियों पर दो संख्यात्मक सूचकांक लुकअप करने के लिए बहुत तेज़ है।

यहाँ परिणाम:

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


3

यह उपयोग पर निर्भर करता है। यदि मामला लुकअप ऑब्जेक्ट्स बहुत तेज है।

यहाँ सरणी और ऑब्जेक्ट लुकअप के प्रदर्शन का परीक्षण करने के लिए एक प्लंकर उदाहरण है।

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

तुम वही देखोगे; 5.000 लंबाई सरणी संग्रह में 5.000 वस्तुओं की तलाश में , मील के पत्थर पर कब्जा3000

हालाँकि वस्तु में 5.000 वस्तुओं की तलाश में 5.000 गुण हैं, केवल 2या 3मिलिसकोन्स लें

ऑब्जेक्ट ट्री बनाने से भी बहुत फर्क नहीं पड़ता


0

मुझे इसी तरह की समस्या थी जो मैं सामना कर रहा हूं जहां मुझे 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

0

यदि आपके पास एक सॉर्ट किया गया सरणी है तो आप एक द्विआधारी खोज कर सकते हैं और यह एक वस्तु लुकअप की तुलना में बहुत तेज़ है, आप यहां मेरा जवाब देख सकते हैं:
जावास्क्रिप्ट का उपयोग करके क्रमबद्ध एरे में तेजी से कैसे खोज करें

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.