जावास्क्रिप्ट सेट बनाम एरे प्रदर्शन


87

यह शायद इसलिए कि सेट्स जावास्क्रिप्ट के लिए अपेक्षाकृत नए हैं, लेकिन मैं स्टैको पर या कहीं और, एक लेख को खोजने में सक्षम नहीं हूं, जो कि जावास्क्रिप्ट में दोनों के बीच प्रदर्शन अंतर के बारे में बात करता है। तो, दोनों के बीच, प्रदर्शन के संदर्भ में, क्या अंतर है? विशेष रूप से, जब हटाने, जोड़ने और पुनरावृत्ति की बात आती है।


1
आप इनका प्रयोग परस्पर नहीं कर सकते। इसलिए उनकी तुलना करना बहुत कम समझ में आता है।
zxms

क्या आप Setऔर / []या के बीच तुलना की बात कर रहे हैं {}?
eithed

2
जोड़ना और पुनरावृत्ति करना बहुत अंतर नहीं करता है, हटाना और - सबसे महत्वपूर्ण बात - लुकिंग में फर्क पड़ता है।
बेर्गी


3
@ झटके-कड़ाई से, ऐरे को या तो आदेश नहीं दिया जाता है, लेकिन उनके सूचकांक का उपयोग उन्हें इलाज करने की अनुमति देता है जैसे कि वे हैं। ;-) एक सेट में मूल्यों का अनुक्रम सम्मिलन क्रम में रखा जाता है।
1

जवाबों:


98

ठीक है, मैंने एक सरणी और एक सेट से तत्वों को जोड़ने, पुनरावृत्ति और हटाने का परीक्षण किया है। मैंने "छोटा" परीक्षण चलाया, जिसमें 10 000 तत्वों का उपयोग किया गया और 100 000 तत्वों का उपयोग करके "बड़ा" परीक्षण किया गया। यहाँ परिणाम हैं।

तत्वों को एक संग्रह में जोड़ना

ऐसा लगता है कि सेट विधि की .pushतुलना में सरणी विधि लगभग 4 गुना तेज है .add, कोई फर्क नहीं पड़ता कि तत्वों की संख्या जोड़ी जा रही है।

संग्रह में तत्वों को बदलना और संशोधित करना

परीक्षण के इस भाग के लिए मैंने forसरणी पर पुनरावृति करने के लिए एक लूप का उपयोग किया और for ofसेट पर पुनरावृति करने के लिए एक लूप। फिर, सरणी पर पुनरावृति तेज थी। इस बार ऐसा लगेगा कि यह तेजी से बढ़ रहा है, इसलिए इसे "छोटे" परीक्षणों के दौरान दो बार और "बड़े" परीक्षणों के दौरान लगभग चार गुना लंबा समय लगा।

एक संग्रह से तत्वों को निकालना

अब यह वह जगह है जहां यह दिलचस्प हो जाता है। मैंने एक forलूप के संयोजन का उपयोग किया और .spliceकुछ तत्वों को सरणी से हटाने के लिए और मैंने उपयोग किया for ofऔर .deleteकुछ तत्वों को सेट से हटा दिया। "छोटे" परीक्षणों के लिए, सेट से वस्तुओं को हटाने के लिए लगभग तीन गुना तेज था (2.6 एमएस बनाम 7.1 एमएस) लेकिन चीजें "बड़े" परीक्षण के लिए काफी बदल गईं जहां केवल सरणी से आइटम हटाने के लिए 1955.1 एमएस लिया। 83.6 एमएस लिया, उन्हें 23 बार तेजी से सेट से हटाने के लिए।

निष्कर्ष

10k तत्वों पर, दोनों परीक्षण तुलनात्मक समय (सरणी: 16.6 एमएस, सेट: 20.7 एमएस) पर चले गए लेकिन 100k तत्वों के साथ काम करते समय, सेट स्पष्ट विजेता था (सरणी: 1974.8 एमएस, सेट: 83.6 एमएस) लेकिन केवल हटाने के कारण ऑपरेशन। अन्यथा सरणी तेज थी। मैं बिल्कुल नहीं कह सकता कि ऐसा क्यों है।

मैंने कुछ हाइब्रिड परिदृश्यों के साथ खेला, जहाँ एक सरणी बनाई गई थी और आबादी थी और फिर एक सेट में परिवर्तित हो गई जहाँ कुछ तत्वों को हटा दिया जाएगा, फिर सेट को एक सरणी में फिर से जोड़ा जाएगा। हालांकि ऐसा करने से सरणी में तत्वों को हटाने की तुलना में बहुत बेहतर प्रदर्शन मिलेगा, एक सेट के बजाय एक सरणी को पॉप्युलेट करने के लाभों को सेट आउट से स्थानांतरित करने के लिए आवश्यक अतिरिक्त प्रसंस्करण समय। अंत में, यह केवल एक सेट के साथ सौदा करने के लिए तेज़ है। फिर भी, यह एक दिलचस्प विचार है, कि अगर कोई किसी बड़े डेटा के लिए डेटा संग्रह के रूप में एक सरणी का उपयोग करने के लिए चुनता है जिसमें डुप्लिकेट नहीं है, तो यह लाभप्रद प्रदर्शन बुद्धिमान हो सकता है, अगर कभी एक में कई तत्वों को निकालने की आवश्यकता होती है ऑपरेशन, सरणी को सेट में बदलने के लिए, निष्कासन ऑपरेशन करें, और सेट को वापस सरणी में परिवर्तित करें।

सरणी कोड:

var timer = function(name) {
  var start = new Date();
  return {
    stop: function() {
      var end = new Date();
      var time = end.getTime() - start.getTime();
      console.log('Timer:', name, 'finished in', time, 'ms');
    }
  }
};

var getRandom = function(min, max) {
  return Math.random() * (max - min) + min;
};

var lastNames = ['SMITH', 'JOHNSON', 'WILLIAMS', 'JONES', 'BROWN', 'DAVIS', 'MILLER', 'WILSON', 'MOORE', 'TAYLOR', 'ANDERSON', 'THOMAS'];

var genLastName = function() {
  var index = Math.round(getRandom(0, lastNames.length - 1));
  return lastNames[index];
};

var sex = ["Male", "Female"];

var genSex = function() {
  var index = Math.round(getRandom(0, sex.length - 1));
  return sex[index];
};

var Person = function() {
  this.name = genLastName();
  this.age = Math.round(getRandom(0, 100))
  this.sex = "Male"
};

var genPersons = function() {
  for (var i = 0; i < 100000; i++)
    personArray.push(new Person());
};

var changeSex = function() {
  for (var i = 0; i < personArray.length; i++) {
    personArray[i].sex = genSex();
  }
};

var deleteMale = function() {
  for (var i = 0; i < personArray.length; i++) {
    if (personArray[i].sex === "Male") {
      personArray.splice(i, 1)
      i--
    }
  }
};

var t = timer("Array");

var personArray = [];

genPersons();

changeSex();

deleteMale();

t.stop();

console.log("Done! There are " + personArray.length + " persons.")

कोड सेट करें:

var timer = function(name) {
    var start = new Date();
    return {
        stop: function() {
            var end  = new Date();
            var time = end.getTime() - start.getTime();
            console.log('Timer:', name, 'finished in', time, 'ms');
        }
    }
};

var getRandom = function (min, max) {
  return Math.random() * (max - min) + min;
};

var lastNames = ['SMITH','JOHNSON','WILLIAMS','JONES','BROWN','DAVIS','MILLER','WILSON','MOORE','TAYLOR','ANDERSON','THOMAS'];

var genLastName = function() {
    var index = Math.round(getRandom(0, lastNames.length - 1));
    return lastNames[index];
};

var sex = ["Male", "Female"];

var genSex = function() {
    var index = Math.round(getRandom(0, sex.length - 1));
    return sex[index];
};

var Person = function() {
	this.name = genLastName();
	this.age = Math.round(getRandom(0,100))
	this.sex = "Male"
};

var genPersons = function() {
for (var i = 0; i < 100000; i++)
	personSet.add(new Person());
};

var changeSex = function() {
	for (var key of personSet) {
		key.sex = genSex();
	}
};

var deleteMale = function() {
	for (var key of personSet) {
		if (key.sex === "Male") {
			personSet.delete(key)
		}
	}
};

var t = timer("Set");

var personSet = new Set();

genPersons();

changeSex();

deleteMale();

t.stop();

console.log("Done! There are " + personSet.size + " persons.")


1
ध्यान रखें, एक सेट का मान डिफ़ॉल्ट रूप से अद्वितीय है। इसलिए, जहां [1,1,1,1,1,1]एक सरणी के लिए लंबाई 6 होगी, एक सेट का आकार 1 होगा। ऐसा लगता है कि आपका कोड वास्तव में सेट के इस विशेषता के कारण प्रत्येक रन पर 100,000 से अधिक आइटमों में बेतहाशा भिन्न आकार के सेट उत्पन्न कर सकता है। आपने शायद कभी ध्यान नहीं दिया क्योंकि आप पूरी स्क्रिप्ट चलने के बाद सेट का आकार नहीं दिखा रहे हैं।
काइलफ़ारिस

6
@KyleFarris जब तक मैं गलत नहीं हूँ, यह सच होगा अगर सेट में डुप्लिकेट थे, जैसे कि आपके उदाहरण में [1, 1, 1, 1, 1], लेकिन चूंकि सेट में प्रत्येक आइटम वास्तव में विभिन्न गुणों वाली एक वस्तु है जिसमें पहला नाम और अंतिम नाम एक सूची से यादृच्छिक रूप से उत्पन्न होता है। सैकड़ों संभावित नाम, एक बेतरतीब ढंग से उत्पन्न उम्र, एक बेतरतीब ढंग से उत्पन्न सेक्स और अन्य बेतरतीब ढंग से उत्पन्न विशेषताएँ ... सेट में दो समान वस्तुओं के होने की संभावनाएं किसी से भी पतली नहीं हैं।
स्नोफ्रॉगदेव

3
वास्तव में, आप इस मामले में सही हैं क्योंकि ऐसा लगता है कि सेट वास्तव में सेट में वस्तुओं से अंतर नहीं करते हैं। तो, वास्तव में आप {foo: 'bar'}सेट में भी समान सटीक वस्तु 10,000x रख सकते हैं और इसका आकार 10,000 होगा। एक ही सरणियों के लिए चला जाता है। ऐसा लगता है कि यह केवल स्केलर मूल्यों (तार, संख्या, बूलियन, आदि) के साथ अद्वितीय है।
काइलफ़ारिस

12
आप सेट में कई बार किसी ऑब्जेक्ट की एक ही सटीक सामग्री रख सकते हैं {foo: 'bar'}, लेकिन सटीक एक ही ऑब्जेक्ट (संदर्भ) नहीं। सूक्ष्म अंतर को इंगित करने वाला मूल्य IMO
SimpleVar

14
आप सेट, 0 (1) लुकअप का उपयोग करने के लिए सबसे महत्वपूर्ण कारण माप को भूल गए। hasबनाम IndexOf
मैग्नस

64

OBSERVATIONS :

  • निष्पादन की धारा के भीतर सेट संचालन को स्नैपशॉट के रूप में समझा जा सकता है।
  • हम एक निश्चित विकल्प से पहले नहीं हैं।
  • सेट क्लास के तत्वों में कोई सुगम सूचकांक नहीं होता है।
  • सेट क्लास एक ऐरे क्लास पूरक है, उन परिदृश्यों में उपयोगी है जहां हमें एक संग्रह को संग्रहीत करने की आवश्यकता होती है, जिस पर बुनियादी जोड़, विलोपन, जाँच और पुनरावृत्ति संचालन लागू होते हैं।

मैं प्रदर्शन के कुछ परीक्षण साझा करता हूं। अपना कंसोल खोलने की कोशिश करें और नीचे दिए गए कोड को कॉपी करें।

एक सरणी बनाना (125000)

var n = 125000;
var arr = Array.apply( null, Array( n ) ).map( ( x, i ) => i );
console.info( arr.length ); // 125000

1. एक सूचकांक का पता लगाना

हमने ऐरे इंडेक्सऑफ के साथ सेट की विधि की तुलना की है:

Array / indexOf (0.281ms) | सेट / है (0.053ms)

// Helpers
var checkArr = ( arr, item ) => arr.indexOf( item ) !== -1;
var checkSet = ( set, item ) => set.has( item );

// Vars
var set, result;

console.time( 'timeTest' );
result = checkArr( arr, 123123 );
console.timeEnd( 'timeTest' );

set = new Set( arr );

console.time( 'timeTest' );
checkSet( set, 123123 );
console.timeEnd( 'timeTest' );

2. एक नया तत्व जोड़ना

हम क्रमशः सेट और ऐरे ऑब्जेक्ट्स के ऐड और पुश तरीकों की तुलना करते हैं:

ऐरे / पुश (1.612ms) | सेट / जोड़ें (0.006ms)

console.time( 'timeTest' );
arr.push( n + 1 );
console.timeEnd( 'timeTest' );

set = new Set( arr );

console.time( 'timeTest' );
set.add( n + 1 );
console.timeEnd( 'timeTest' );

console.info( arr.length ); // 125001
console.info( set.size ); // 125001

3. किसी तत्व को हटाना

तत्वों को हटाते समय, हमें यह ध्यान रखना होगा कि एरियर और सेट समान परिस्थितियों में शुरू न हों। ऐरे में मूल विधि नहीं है, इसलिए एक बाहरी कार्य आवश्यक है।

Array / deleteFromArr (0.356ms) | सेट / निकालें (0.019ms)

var deleteFromArr = ( arr, item ) => {
    var i = arr.indexOf( item );
    i !== -1 && arr.splice( i, 1 );
};

console.time( 'timeTest' );
deleteFromArr( arr, 123123 );
console.timeEnd( 'timeTest' );

set = new Set( arr );

console.time( 'timeTest' );
set.delete( 123123 );
console.timeEnd( 'timeTest' );

पूरा लेख यहाँ पढ़ें


4
Array.indexOf को उनके समकक्ष होने के लिए Array.includes होना चाहिए। मुझे फ़ायरफ़ॉक्स पर बहुत अलग नंबर मिल रहे हैं।
कगारनिक

2
मैं Object.includes बनाम Set.has तुलना में दिलचस्पी होगी ...
लियोपोल्ड क्रिस्टजन्सन

1
@ LeopoldKristjansson मैंने एक तुलना परीक्षण नहीं लिखा था, लेकिन हमने एक उत्पादन साइट में 24k आइटम के साथ सरणियों के साथ किया और Array.includes से Set.has पर स्विच करना एक जबरदस्त प्रदर्शन को बढ़ावा दिया था!
sedot

3

मेरा अवलोकन यह है कि एक सेट हमेशा दो सरणियों के साथ बेहतर होता है, जो बड़े सरणियों को ध्यान में रखते हैं:

क) forअरैज़ से सेट का निर्माण लूप में एक पूर्व निर्धारित लंबाई के साथ किया जाना चाहिए ।

धीमी गति से (जैसे 18ms) new Set(largeArray)

तेज (जैसे 6ms) const SET = new Set(); const L = largeArray.length; for(var i = 0; i<L; i++) { SET.add(largeArray[i]) }

बी) Iterating उसी तरह से किया जा सकता है क्योंकि यह for ofलूप से भी तेज है ...

देख Https://jsfiddle.net/0j2gkae7/5/

करने के लिए एक वास्तविक जीवन की तुलना के लिए difference(), intersection(), union()और uniq()40.000 तत्वों के साथ (+ उनके iteratee साथियों आदि)


3

बेंचमार्क किए गए Iteration का स्क्रीनशॉटआपके प्रश्न के पुनरावृत्ति भाग के लिए, मैंने हाल ही में इस परीक्षण को चलाया और पाया कि सेट ने 10,000 वस्तुओं के एक एरियर को बेहतर बना दिया (लगभग 10x ऑपरेशन एक ही समय सीमा में हो सकते हैं)। और ब्राउजर के आधार पर या तो टेस्ट के लिए एक तरह से Object.hasOwnProperty को हराया या हार गया।

सेट और ऑब्जेक्ट दोनों के पास ओ (1) के लिए परिशोधित होने के लिए उनका "" विधि प्रदर्शन है, लेकिन ब्राउज़र के कार्यान्वयन के आधार पर एकल ऑपरेशन में अधिक या अधिक समय लग सकता है। ऐसा लगता है कि अधिकांश ब्राउज़र सेट.हास () की तुलना में तेजी से ऑब्जेक्ट को लागू करते हैं। यहां तक ​​कि Object.hasOwnProperty जिसमें कुंजी पर एक अतिरिक्त चेक शामिल है, Set.has () से कम से कम क्रोम v86 पर मेरे लिए लगभग 5% तेज है।

https://jsperf.com/set-has-vs-object-hasownproperty-vs-array-includes/1

अपडेट: 11/11/2020: https://jsbench.me/irkhdxnoqa/2

यदि आप विभिन्न ब्राउज़रों / वातावरणों के साथ अपना परीक्षण चलाना चाहते हैं।


इसी तरह मैं एक सरणी बनाम सेट में आइटम जोड़ने और हटाने के लिए एक बेंचमार्क जोड़ूंगा।


4
कृपया अपने उत्तरों में लिंक का उपयोग न करें (जब तक कि आधिकारिक पुस्तकालयों से लिंक न हो) क्योंकि ये लिंक टूट सकते हैं - जैसा कि आपके मामले में हुआ था। आप लिंक 404 है।
गिल एप्सटेन

मैंने एक लिंक का उपयोग किया लेकिन जब यह उपलब्ध था तब आउटपुट की प्रतिलिपि भी बनाई। यह दुर्भाग्यपूर्ण है कि उन्होंने अपनी लिंकिंग रणनीति को इतनी जल्दी बदल दिया।
ज़र्गोल्ड

एक स्क्रीनशॉट और एक नई जेएस प्रदर्शन वेबसाइट के साथ अब पोस्ट को अपडेट करें: jsbench.me
ज़रागॉल्ड

-5
console.time("set")
var s = new Set()
for(var i = 0; i < 10000; i++)
  s.add(Math.random())
s.forEach(function(e){
  s.delete(e)
})
console.timeEnd("set")
console.time("array")
var s = new Array()
for(var i = 0; i < 10000; i++)
  s.push(Math.random())
s.forEach(function(e,i){
  s.splice(i)
})
console.timeEnd("array")

10K आइटम पर उन तीन आपरेशनों ने मुझे दिया:

set: 7.787ms
array: 2.388ms

@Bergi यही मैंने शुरू में भी सोचा था, लेकिन यह करता है।
zxms

1
@zerkms: "काम" को परिभाषित करें :-) हाँ, सरणी खाली हो जाएगा के बाद forEach, लेकिन शायद जिस तरह से आपको उम्मीद नहीं थी। यदि कोई तुलनीय व्यवहार करना चाहता है, तो यह भी होना चाहिए s.forEach(function(e) { s.clear(); })
बेर्गी

1
ठीक है, यह कुछ करता है, न कि क्या इरादा है: यह सूचकांक I और अंत के बीच के सभी तत्वों को हटा देता है । यह deleteसेट पर क्या करता है की तुलना नहीं करता है।
त्रिकोट

@ बरगी ओह ठीक है, यह सिर्फ 2 पुनरावृत्तियों में सब कुछ हटा देता है। मेरी गलती।
zmsms

4
1 पुनरावृति में। splice(0)एक सरणी खाली करता है।
23
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.