Mungkin karena Sets relatif baru di Javascript tetapi saya belum dapat menemukan artikel, di StackO atau di mana pun, yang berbicara tentang perbedaan kinerja antara keduanya di Javascript. Lantas, apa perbedaan dari segi performa di antara keduanya? Secara khusus, ketika menyangkut penghapusan, penambahan, dan pengulangan.
javascript
arrays
performance
set
iteration
snowfrogdev
sumber
sumber
Set
dan[]
atau{}
?Jawaban:
Oke, saya telah menguji menambahkan, mengulang, dan menghapus elemen dari array dan set. Saya menjalankan tes "kecil", menggunakan 10.000 elemen dan tes "besar", menggunakan 100.000 elemen. Berikut hasilnya.
Menambahkan elemen ke koleksi
Tampaknya
.push
metode array sekitar 4 kali lebih cepat daripada.add
metode set, tidak peduli jumlah elemen yang ditambahkan.Iterasi dan modifikasi elemen dalam koleksi
Untuk bagian pengujian ini saya menggunakan
for
loop untuk mengulang array danfor of
loop untuk mengulang set. Sekali lagi, pengulangan array lebih cepat. Kali ini akan terlihat secara eksponensial sehingga memakan waktu dua kali lebih lama selama tes "kecil" dan hampir empat kali lebih lama selama tes "besar".Menghapus elemen dari koleksi
Sekarang di sinilah menjadi menarik. Saya menggunakan kombinasi
for
loop dan.splice
untuk menghapus beberapa elemen dari array dan saya menggunakanfor of
dan.delete
untuk menghapus beberapa elemen dari set. Untuk pengujian "kecil", sekitar tiga kali lebih cepat untuk menghapus item dari set (2,6 md vs 7,1 md) tetapi banyak hal berubah secara drastis untuk pengujian "besar" di mana dibutuhkan 1955,1 md untuk menghapus item dari larik sementara itu hanya butuh 83,6 ms untuk menghapusnya dari set, 23 kali lebih cepat.Kesimpulan
Pada 10k elemen, kedua tes berjalan dengan waktu yang sebanding (array: 16.6 ms, set: 20.7 ms) tetapi ketika berhadapan dengan elemen 100k, set adalah pemenang yang jelas (array: 1974.8 ms, set: 83.6 ms) tetapi hanya karena penghapusan operasi. Jika tidak, susunannya lebih cepat. Saya tidak bisa mengatakan dengan tepat mengapa itu terjadi.
Saya bermain-main dengan beberapa skenario hybrid di mana sebuah array dibuat dan diisi dan kemudian diubah menjadi satu set di mana beberapa elemen akan dihapus, set tersebut kemudian akan diubah kembali menjadi sebuah array. Meskipun melakukan ini akan memberikan kinerja yang jauh lebih baik daripada menghapus elemen dalam larik, waktu pemrosesan tambahan yang diperlukan untuk mentransfer ke dan dari suatu set lebih besar daripada keuntungan dari mengisi larik daripada satu set. Pada akhirnya, lebih cepat hanya menangani satu set. Namun, ini adalah ide yang menarik, bahwa jika seseorang memilih untuk menggunakan array sebagai pengumpulan data untuk beberapa data besar yang tidak memiliki duplikat, itu bisa menjadi kinerja yang menguntungkan, jika ada kebutuhan untuk menghapus banyak elemen dalam satu operasi, untuk mengonversi larik menjadi satu set, melakukan operasi penghapusan, dan mengonversi set kembali menjadi larik.
Kode array:
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.")
Atur kode:
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.")
sumber
[1,1,1,1,1,1]
untuk sebuah larik akan memiliki panjang 6, satu set akan memiliki ukuran 1. Sepertinya kode Anda sebenarnya dapat menghasilkan set dengan ukuran yang sangat berbeda dari 100.000 item dalam ukuran pada setiap proses karena sifat Set ini. Anda mungkin tidak pernah menyadarinya karena Anda tidak menampilkan ukuran set hingga seluruh skrip dijalankan.[1, 1, 1, 1, 1]
, tetapi karena setiap item dalam set sebenarnya adalah objek dengan berbagai properti termasuk nama depan dan nama belakang yang dihasilkan secara acak dari daftar dari ratusan kemungkinan nama, usia yang dibuat secara acak, jenis kelamin yang dibuat secara acak, dan atribut yang dihasilkan secara acak lainnya ... kemungkinan memiliki dua objek yang identik dalam set sangat kecil.{foo: 'bar'}
10.000x dalam himpunan dan akan memiliki ukuran 10.000. Hal yang sama berlaku untuk array. Tampaknya itu hanya unik dengan nilai skalar (string, angka, boolean, dll ..).{foo: 'bar'}
berkali-kali dalam Kumpulan, tetapi bukan objek yang sama persis (referensi).has
vsIndexOf
.Saya membagikan beberapa tes kinerja. Coba buka konsol Anda dan tempelkan kode di bawah ini.
Membuat array (125000)
var n = 125000; var arr = Array.apply( null, Array( n ) ).map( ( x, i ) => i ); console.info( arr.length ); // 125000
1. Menemukan Indeks
Kami membandingkan metode Set dengan Array indexOf:
// 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. Menambahkan elemen baru
Kami membandingkan metode add dan push dari objek Set dan Array masing-masing:
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. Menghapus sebuah elemen
Saat menghapus elemen, kita harus ingat bahwa Array dan Set tidak dimulai dalam kondisi yang sama. Array tidak memiliki metode asli, jadi diperlukan fungsi eksternal.
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' );
Baca artikel lengkapnya di sini
sumber
Pengamatan saya adalah bahwa Set selalu lebih baik dengan dua perangkap untuk array besar:
a) Pembuatan Set dari Array harus dilakukan dalam satu
for
lingkaran dengan panjang yang telah ditentukan sebelumnya.lambat (mis. 18ms)
new Set(largeArray)
cepat (mis. 6ms)
const SET = new Set(); const L = largeArray.length; for(var i = 0; i<L; i++) { SET.add(largeArray[i]) }
b) Iterasi bisa dilakukan dengan cara yang sama karena juga lebih cepat dari
for of
loop ...Lihat https://jsfiddle.net/0j2gkae7/5/
untuk kehidupan perbandingan nyata untuk
difference()
,intersection()
,union()
danuniq()
(+ sahabat iteratee mereka dll) dengan 40.000 elemensumber
Untuk bagian iterasi dari pertanyaan Anda, saya baru-baru ini menjalankan tes ini dan menemukan bahwa Set jauh mengungguli Array 10.000 item (sekitar 10x operasi dapat terjadi dalam jangka waktu yang sama). Dan tergantung pada browser apakah mengalahkan atau kalah dari Object.hasOwnProperty dalam tes sejenisnya.
Baik Set dan Object memiliki metode "has" yang bekerja dalam apa yang tampaknya diamortisasi ke O (1), tetapi tergantung pada implementasi browser, operasi tunggal bisa memakan waktu lebih lama atau lebih cepat. Tampaknya sebagian besar browser mengimplementasikan kunci di Object lebih cepat daripada Set.has (). Bahkan Object.hasOwnProperty yang menyertakan pemeriksaan tambahan pada kunci sekitar 5% lebih cepat dari Set.has () setidaknya bagi saya di Chrome v86.
https://jsperf.com/set-has-vs-object-hasownproperty-vs-array-includes/1
Pembaruan: 11/11/2020: https://jsbench.me/irkhdxnoqa/2
Jika Anda ingin menjalankan pengujian Anda sendiri dengan browser / lingkungan yang berbeda.
Demikian pula saya akan menambahkan patokan untuk menambahkan item ke array vs set dan menghapus.
sumber
Hanya Pencarian Properti, sedikit atau nol tulis
Jika pencarian properti adalah perhatian utama Anda, berikut beberapa angkanya.
Tes JSBench https://jsbench.me/3pkjlwzhbr/1
Tampilkan cuplikan kode
// https://jsbench.me/3pkjlwzhbr/1 // https://docs.google.com/spreadsheets/d/1WucECh5uHlKGCCGYvEKn6ORrQ_9RS6BubO208nXkozk/edit?usp=sharing // JSBench forked from https://jsbench.me/irkhdxnoqa/2 var theArr = Array.from({ length: 10000 }, (_, el) => el) var theSet = new Set(theArr) var theObject = Object.assign({}, ...theArr.map(num => ({ [num]: true }))) var theMap = new Map(theArr.map(num => [num, true])) var theTarget = 9000 // Array function isTargetThereFor(arr, target) { const len = arr.length for (let i = 0; i < len; i++) { if (arr[i] === target) { return true } } return false } function isTargetThereForReverse(arr, target) { const len = arr.length for (let i = len; i > 0; i--) { if (arr[i] === target) { return true } } return false } function isTargetThereIncludes(arr, target) { return arr.includes(target) } // Set function isTargetThereSet(numberSet, target) { return numberSet.has(target) } // Object function isTargetThereHasOwnProperty(obj, target) { return obj.hasOwnProperty(target) } function isTargetThereIn(obj, target) { return target in obj } function isTargetThereSelectKey(obj, target) { return obj[target] } // Map function isTargetThereMap(numberMap, target) { return numberMap.has(target) }
for
lingkaranfor
loop (terbalik)array.includes(target)
set.has(target)
obj.hasOwnProperty(target)
target in obj
<- 1,29% lebih lambatobj[target]
<- tercepatmap.has(target)
<- 2.94% lebih lambatHasil dari browser lain dipersilahkan, perbarui jawaban ini.
Anda dapat menggunakan spreadsheet ini untuk membuat tangkapan layar yang bagus.
Tes JSBench bercabang dari jawaban Zargold.
sumber
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")
Tiga operasi pada 10 ribu item itu memberi saya:
set: 7.787ms array: 2.388ms
sumber
forEach
, tapi mungkin tidak seperti yang Anda harapkan. Jika seseorang menginginkan perilaku yang sebanding, seharusnyas.forEach(function(e) { s.clear(); })
juga demikian.delete
dilakukan di Set.splice(0)
mengosongkan array.