क्या जावास्क्रिप्ट सरणियाँ विरल हैं?


97

यदि मैं वर्तमान समय को एक इंडेक्स के रूप में सरणी में उपयोग करता हूं:

array[Date.getTime()] = value;

क्या दुभाषिया 0 से अब तक के सभी तत्वों को तुरंत बदल देगा? क्या अलग-अलग ब्राउज़र इसे अलग तरीके से करते हैं?

मुझे याद है कि AIX कर्नेल में एक बग हुआ करता था , जो अनुरोध पर छद्म ttys बनाता था, लेकिन यदि आपने किया, तो कहें, "गूंज> / देव / pty10000000000" यह / dev / pty0, / dev / pty1 का निर्माण करेगा, .... और फिर मुर्दा हो गया। यह व्यापार शो में मजेदार था, लेकिन मैं नहीं चाहता कि मेरे ग्राहकों के साथ ऐसा हो।


1
ऐसा करने के लिए एक संभावित नकारात्मक पक्ष फायरबग में डिबगिंग की कठिनाई है। सरणी पर एक लॉग स्टेटमेंट केवल सरणी में पहले 1000 तत्वों को सूचीबद्ध करेगा, जो सभी "अपरिभाषित" होंगे। इसके अलावा, array.length आपको बताएगा कि आपके सरणी में एन तत्व हैं, भले ही एन -1 केवल "भूत" अपरिभाषित मूल्य हैं।
माइकल बटलर

क्रोम में अब डीबगिंग ठीक है - यहाँ कंसोल आउटपुट का एक उदाहरण है: [खाली × 9564, ऑब्जेक्ट, खाली × 105, ऑब्जेक्ट, खाली × 10, ऑब्जेक्ट, खाली × 12, ऑब्जेक्ट, खाली × 9, ऑब्जेक्ट, खाली × 21, वस्तु, खाली × 9, वस्तु]
jsalvata

जवाबों:


40

वास्तव में जावास्क्रिप्ट सरणियों को कैसे लागू किया जाता है, यह ब्राउज़र से ब्राउज़र में भिन्न होता है, लेकिन वे आम तौर पर एक दुर्लभ कार्यान्वयन पर वापस आते हैं - सबसे अधिक संभावना है कि नियमित वस्तुओं की संपत्ति के उपयोग के लिए उपयोग किया जाता है - यदि वास्तविक सरणी का उपयोग करना अक्षम हो।

आपको विशिष्ट कार्यान्वयन के बारे में अधिक जानकारी के साथ किसी से पूछना होगा कि घने से विरल में शिफ्ट को ट्रिगर करने के लिए क्या जवाब है, लेकिन आपका उदाहरण पूरी तरह से सुरक्षित होना चाहिए। यदि आप एक घनी सरणी प्राप्त करना चाहते हैं, तो आपको स्पष्ट लंबाई के तर्क के साथ कंस्ट्रक्टर को कॉल करना चाहिए और आशा है कि आप वास्तव में एक प्राप्त करेंगे।

Olliej द्वारा अधिक विस्तृत विवरण के लिए इस उत्तर को देखें ।


1
मुझे नहीं लगता कि अगर आप कुछ कहते हैं तो आपको वास्तव में एक सघन सरणी मिलती है foo = new Array(10000)। हालांकि, यह काम करने वाला है foo = Array.apply(null, {length: 10});:।
डबलऑर्ट

70

हाँ वे हैं। वे वास्तव में आंतरिक रूप से हैश टेबल होते हैं, इसलिए आप न केवल बड़े पूर्णांक का उपयोग कर सकते हैं, बल्कि स्ट्रिंग्स, फ्लोट्स या अन्य वस्तुओं का भी उपयोग कर सकते हैं। सभी कुंजियाँ toString()हैश में जोड़े जाने से पहले तार के माध्यम से परिवर्तित हो जाती हैं । आप कुछ परीक्षण कोड के साथ इसकी पुष्टि कर सकते हैं:

<script>
  var array = [];
  array[0] = "zero";
  array[new Date().getTime()] = "now";
  array[3.14] = "pi";

  for (var i in array) {
      alert("array["+i+"] = " + array[i] + ", typeof("+i+") == " + typeof(i));
  }
</script>

प्रदर्शित करता है:

array[0] = zero, typeof(0) == string
array[1254503972355] = now, typeof(1254503972355) == string
array[3.14] = pi, typeof(3.14) == string

ध्यान दें कि मैंने for...inसिंटैक्स का उपयोग कैसे किया , जो आपको केवल उन सूचकांकों को देता है जो वास्तव में परिभाषित हैं। यदि आप for (var i = 0; i < array.length; ++i)पुनरावृत्ति की अधिक सामान्य शैली का उपयोग करते हैं तो आपको स्पष्ट रूप से गैर-मानक सरणी सूचकांकों के साथ समस्या होगी।


9
अधिकांश JS कार्यान्वयन यदि संभव हो तो एक वास्तविक सरणी में संख्यात्मक रूप से अनुक्रमित गुणों को संग्रहीत करते हैं; हालांकि, इसके पीछे-पीछे जादू है, हालांकि: एक भाषा के दृष्टिकोण से, सरणियां एक जादू की lengthसंपत्ति के साथ नियमित वस्तुएं हैं
क्रिस्टोफ़

7
@ जॉन: lengthकेवल for..inलूप में अदृश्य है क्योंकि इसमें DontEnumध्वज सेट है; ईएस 5 में, संपत्ति विशेषता को कहा जाता है enumerableऔर स्पष्ट रूप से सेट किया जा सकता हैObject.defineProperty()
क्रिस्टोफ़

14
जावास्क्रिप्ट में सभी ऑब्जेक्ट कुंजियाँ हमेशा होती हैं String; कुछ और जो आपने सबस्क्रिप्ट में डाल दिया है toString()-ed। बड़ी संख्या के पूर्णांक आवेग के साथ इसे मिलाएं और इसका मतलब है कि यदि आप सेट करते हैं a[9999999999999999]=1, तो a[10000000000000000]1 होगा (और कई और अधिक आश्चर्यजनक व्यवहार)। गैर-पूर्णांक को कुंजियों के रूप में उपयोग करना बहुत नासमझी है, और मनमानी वस्तुएं सही हैं।
बॉब

71
तब आप केवल स्ट्रिंग्स को ऑब्जेक्ट कुंजियों के रूप में उपयोग करते हैं, और नहीं, कम नहीं। स्ट्रिंग वह प्रकार होगा जिसका आप उपयोग करेंगे, और कुंजी का प्रकार स्ट्रिंग होगा। पूर्णांक का उपयोग न करें, न तो आप गैर-पूर्णांक का उपयोग करें, सिवाय इसके कि आप फिर से स्ट्रिंग में डालने के लिए आगे बढ़ें। मनमाना वस्तुएं बाहर हैं।
क्रिसेंट फ्रेश

8
सरणी सूचकांक पूर्णांक होना चाहिए। सरणी [३.१४] = पी काम करता है क्योंकि एरे ऑब्जेक्ट से इनरेट करता है। उदाहरण: var x = []; x [.1] = ५; तब x की लंबाई 0 है।
माइक ब्लैंडफ़ोर्ड

10

आप इस प्रकार की चीज़ के लिए डिज़ाइन किए गए जावास्क्रिप्ट सिंटैक्स का उपयोग करके समस्या से बच सकते हैं। आप इसे एक शब्दकोश के रूप में मान सकते हैं, फिर भी "इन ... इन ..." सिंटैक्स आपको उन सभी को हथियाने देगा।

var sparse = {}; // not []
sparse["whatever"] = "something";

7

जावास्क्रिप्ट ऑब्जेक्ट्स विरल हैं, और एरेज़ केवल एक ऑटो-मेंटेनेंस लेंथ प्रॉपर्टी के साथ विशेष ऑब्जेक्ट्स हैं (जो वास्तव में सबसे बड़े इंडेक्स से बड़ा है, कि परिभाषित तत्वों की संख्या) और कुछ अतिरिक्त तरीके। आप या तो सुरक्षित हैं; एक सरणी का उपयोग करें यदि आपको इसकी अतिरिक्त सुविधाओं की आवश्यकता है, और अन्यथा एक वस्तु।


4
वह भाषा के दृष्टिकोण से है; कार्यान्वयन वास्तव में घने संख्यात्मक गुणों को स्टोर करने के लिए वास्तविक सरणियों का उपयोग करते हैं
क्रिस्टोफ़

6

जावास्क्रिप्ट के साथ आमतौर पर सच है, जवाब, "यह थोड़ा अजीब है ..."

मेमोरी उपयोग को परिभाषित नहीं किया गया है और किसी भी कार्यान्वयन को बेवकूफ बनाने की अनुमति है। सिद्धांत const a = []; a[1000000]=0;रूप में, मेमोरी के मेगाबाइट्स को जला सकता है, जैसा कि हो सकता है const a = [];। व्यवहार में, यहां तक ​​कि Microsoft उन कार्यान्वयनों से बचता है।

जस्टिन लव बताते हैं, लंबाई विशेषता उच्चतम सूचकांक सेट है। यदि इसका पूर्णांक इंडेक्स पूर्णांक है तो इसे अपडेट करें।

तो, सरणी विरल है। बिल्ट-इन फंक्शन्स जैसे कम करना (), मैथ.मैक्स (), और "फॉर ... ऑफ" संभव पूर्णांक सूचकांकों की पूरी रेंज के माध्यम से 0 से लंबाई तक चलेगा, जो कि 'अपरिभाषित' लौटाता है। BUT 'के लिए ...' में लूप्स आपकी अपेक्षा के अनुसार कर सकते हैं, केवल परिभाषित कुंजी पर जाकर।

यहाँ Node.js का उपयोग करके एक उदाहरण दिया गया है:

"use strict";
const print = console.log;

let a = [0, 10];
// a[2] and a[3] skipped
a[4] = 40;
a[5] = undefined;  // which counts towards setting the length
a[31.4] = 'ten pi';  // doesn't count towards setting the length
a['pi'] = 3.14;
print(`a.length= :${a.length}:, a = :${a}:`);
print(`Math.max(...a) = :${Math.max(a)}: because of 'undefined values'`);
for (let v of a) print(`v of a; v=:${v}:`);
for (let i in a) print(`i in a; i=:${i}: a[i]=${a[i]}`);

दे रही है:

a.length= :6:, a = :0,10,,,40,:
Math.max(...a) = :NaN: because of 'undefined values'
v of a; v=:0:
v of a; v=:10:
v of a; v=:undefined:
v of a; v=:undefined:
v of a; v=:40:
v of a; v=:undefined:
i in a; i=:0: a[i]=0
i in a; i=:1: a[i]=10
i in a; i=:4: a[i]=40
i in a; i=:5: a[i]=undefined
i in a; i=:31.4: a[i]=ten pi
i in a; i=:pi: a[i]=3.14

परंतु। ऐरे के साथ अधिक कोने वाले मामले हैं जिनका अभी तक उल्लेख नहीं किया गया है।


2

गैर-मानक प्रक्रिया . memoryUsage () के साथ NodeJS के लिए स्पार्सनेस (या घनत्व) की पुष्टि की जा सकती है ।

कभी-कभी नोड सरणी विरल रखने के लिए पर्याप्त चतुर होता है:

Welcome to Node.js v12.15.0.
Type ".help" for more information.
> console.log(`The script is using approximately ${Math.round(process.memoryUsage().heapUsed / 1024 / 1024 * 100) / 100} MB`)
The script is using approximately 3.07 MB
undefined
> array = []
[]
> array[2**24] = 2**24
16777216
> array
[ <16777216 empty items>, 16777216 ]
> console.log(`The script is using approximately ${Math.round(process.memoryUsage().heapUsed / 1024 / 1024 * 100) / 100} MB`)
The script is using approximately 2.8 MB
undefined

कभी-कभी नोड इसे घना बनाने का विकल्प चुनता है (यह व्यवहार भविष्य में अच्छी तरह से अनुकूलित हो सकता है):

> otherArray = Array(2**24)
[ <16777216 empty items> ]
> console.log(`The script is using approximately ${Math.round(process.memoryUsage().heapUsed / 1024 / 1024 * 100) / 100} MB`)
The script is using approximately 130.57 MB
undefined

फिर विरल:

> yetAnotherArray = Array(2**32-1)
[ <4294967295 empty items> ]
> console.log(`The script is using approximately ${Math.round(process.memoryUsage().heapUsed / 1024 / 1024 * 100) / 100} MB`)
The script is using approximately 130.68 MB
undefined

तो शायद मूल AIX कर्नेल बग के लिए महसूस करने के लिए एक घने सरणी का उपयोग करके रेंज- एक जैसे के साथ मजबूर होना पड़ सकता है :

> denseArray = [...Array(2**24).keys()]
[
   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,
  12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
  24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
  36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
  48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
  60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
  72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
  84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
  96, 97, 98, 99,
  ... 16777116 more items
]
> console.log(`The script is using approximately ${Math.round(process.memoryUsage().heapUsed / 1024 / 1024 * 100) / 100} MB`);
The script is using approximately 819.94 MB
undefined

क्योंकि क्यों न इसे खत्म कर दिया जाए?

> tooDenseArray = [...Array(2**32-1).keys()]

<--- Last few GCs --->

[60109:0x1028ca000]   171407 ms: Scavenge 1072.7 (1090.0) -> 1056.7 (1090.0) MB, 0.2 / 0.0 ms  (average mu = 0.968, current mu = 0.832) allocation failure 
[60109:0x1028ca000]   171420 ms: Scavenge 1072.7 (1090.0) -> 1056.7 (1090.0) MB, 0.2 / 0.0 ms  (average mu = 0.968, current mu = 0.832) allocation failure 
[60109:0x1028ca000]   171434 ms: Scavenge 1072.7 (1090.0) -> 1056.7 (1090.0) MB, 0.2 / 0.0 ms  (average mu = 0.968, current mu = 0.832) allocation failure 


<--- JS stacktrace --->

==== JS stack trace =========================================

    0: ExitFrame [pc: 0x100931399]
    1: StubFrame [pc: 0x1008ee227]
    2: StubFrame [pc: 0x100996051]
Security context: 0x1043830808a1 <JSObject>
    3: /* anonymous */ [0x1043830b6919] [repl:1] [bytecode=0x1043830b6841 offset=28](this=0x104306fc2261 <JSGlobal Object>)
    4: InternalFrame [pc: 0x1008aefdd]
    5: EntryFrame [pc: 0x1008aedb8]
    6: builtin exit frame: runInThisContext(this=0x104387b8cac1 <ContextifyScript map = 0x1043...

FATAL ERROR: invalid array length Allocation failed - JavaScript heap out of memory

Writing Node.js report to file: report.20200220.220620.60109.0.001.json
Node.js report completed
 1: 0x10007f4b9 node::Abort() [/Users/pzrq/.nvm/versions/node/v12.15.0/bin/node]
 2: 0x10007f63d node::OnFatalError(char const*, char const*) [/Users/pzrq/.nvm/versions/node/v12.15.0/bin/node]
 3: 0x100176a27 v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [/Users/pzrq/.nvm/versions/node/v12.15.0/bin/node]
 4: 0x1001769c3 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/Users/pzrq/.nvm/versions/node/v12.15.0/bin/node]
 5: 0x1002fab75 v8::internal::Heap::FatalProcessOutOfMemory(char const*) [/Users/pzrq/.nvm/versions/node/v12.15.0/bin/node]
 6: 0x1005f3e9b v8::internal::Runtime_FatalProcessOutOfMemoryInvalidArrayLength(int, unsigned long*, v8::internal::Isolate*) [/Users/pzrq/.nvm/versions/node/v12.15.0/bin/node]
 7: 0x100931399 Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit [/Users/pzrq/.nvm/versions/node/v12.15.0/bin/node]
 8: 0x1008ee227 Builtins_IterableToList [/Users/pzrq/.nvm/versions/node/v12.15.0/bin/node]
Abort trap: 6

1
अच्छा है, और मैं अपने दस साल पुराने प्रश्न को आश्चर्यचकित कर रहा हूँ अभी भी प्रासंगिक है!
बेरी

1

वे हो सकते हैं, लेकिन वे हमेशा नहीं होते हैं, और जब वे नहीं होते हैं तो वे बेहतर प्रदर्शन कर सकते हैं।

यहाँ एक सरणी उदाहरण में सूचकांक स्पार्सिटी के परीक्षण के तरीके के बारे में चर्चा की गई है: https://benmccormick.org/2018/06/19/code-golf-sparse-arrays/

यह कोड गोल्फ (सबसे कम वर्ण) विजेता है:

let isSparse = a => !!a.reduce(x=>x-1,a.length)

मूल रूप से अनुक्रमित प्रविष्टियों के लिए सरणी चलना, जबकि लंबाई मान को कम करना और !!मिथ्या / सत्य संख्यात्मक परिणाम के कठोर बूलियन को वापस करना (यदि संचायक शून्य करने के लिए सभी तरह से घटाया जाता है, तो सूचकांक पूरी तरह से तैयार नहीं है और विरल नहीं है)। ऊपर से चार्ल्स मेरियम के कैविट को माना जाना चाहिए और यह कोड उन्हें संबोधित नहीं करता है, लेकिन वे हैश स्ट्रिंग प्रविष्टियों पर लागू होते हैं जो तब हो सकते हैं जब तत्वों को असाइन करना arr[var]= (something)जहां var एक पूर्णांक नहीं था।

सूचकांक विरलता के बारे में परवाह करने के कारण प्रदर्शन पर इसके प्रभाव हैं, जो स्क्रिप्ट इंजन के बीच भिन्न हो सकते हैं, यहां सरणी निर्माण / .initialization के बारे में एक महान चर्चा है: जावास्क्रिप्ट की घोषणा करते समय "एरे ()" और "[]" के बीच क्या अंतर है। सरणी?

उस पोस्ट के हालिया उत्तर में इस गहरी डुबकी का एक लिंक है कि कैसे V8 ऐंठन से बचने के लिए (पुनः) परीक्षण करने के लिए उन्हें टैग करके ऐरे को अनुकूलित करने का प्रयास करता है: https://v8.dev/blog/elements-kinds । ब्लॉग पोस्ट Sept '17 से है और सामग्री कुछ परिवर्तन के अधीन है, लेकिन दिन-प्रतिदिन के विकास के लिए निहितार्थों का टूटना उपयोगी और स्पष्ट है।

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