如何使用 Set 來提高JS代碼的性能

摘要: 高效使用Set仁热!

Fundebug經(jīng)授權(quán)轉(zhuǎn)載耙厚,版權(quán)歸原作者所有。

為了保證的可讀性仰楚,本文采用意譯而非直譯隆判。

我確信有很多開發(fā)人員堅(jiān)持使用基本的全局對象:數(shù)字犬庇,字符串,對象侨嘀,數(shù)組和布爾值臭挽。對于許多用例,這些都是需要的咬腕。 但是如果想讓你的代碼盡可能快速和可擴(kuò)展欢峰,那么這些基本類型并不總是足夠好。

在本文中涨共,我們將討論JS 中Set對象如何讓代碼更快—?特別擴(kuò)展性方便纽帖。 ArraySet工作方式存在大量的交叉。但是使用Set會比Array在代碼運(yùn)行速度更有優(yōu)勢举反。

Set 有何不同

最根本的區(qū)別是數(shù)組是一個索引集合懊直,這說明數(shù)組中的數(shù)據(jù)值按索引排序。

const arr = [A, B, C, D];
console.log(arr.indexOf(A)); // Result: 0
console.log(arr.indexOf(C)); // Result: 2

相比之下火鼻,set是一個鍵的集合室囊。set不使用索引,而是使用鍵對數(shù)據(jù)排序魁索。set 中的元素按插入順序是可迭代的融撞,它不能包含任何重復(fù)的數(shù)據(jù)。換句話說粗蔚,set中的每一項(xiàng)都必須是惟一的尝偎。

主要的好處是什么

set 相對于數(shù)組有幾個優(yōu)勢,特別是在運(yùn)行時間方面:

  • 查看元素:使用indexOf()includes()檢查數(shù)組中的項(xiàng)是否存在是比較慢的支鸡。
  • 刪除元素:在Set中,可以根據(jù)每項(xiàng)的的 value 來刪除該項(xiàng)趁窃。在數(shù)組中牧挣,等價的方法是使用基于元素的索引的splice()。與前一點(diǎn)一樣醒陆,依賴于索引的速度很慢瀑构。
  • 保存 NaN:不能使用indexOf()includes() 來查找值 NaN,而 Set 可以保存此值刨摩。
  • 刪除重復(fù)項(xiàng):Set對象只存儲惟一的值,如果不想有重復(fù)項(xiàng)存在寺晌,相對于數(shù)組的一個顯著優(yōu)勢,因?yàn)閿?shù)組需要額外的代碼來處理重復(fù)澡刹。

時間復(fù)雜度呻征?

數(shù)組用來搜索元素的方法時間復(fù)雜度為0(N)。換句話說罢浇,運(yùn)行時間的增長速度與數(shù)據(jù)大小的增長速度相同陆赋。

相比之下沐祷,Set用于搜索、刪除和插入元素的方法的時間復(fù)雜度都只有O(1)攒岛,這意味著數(shù)據(jù)的大小實(shí)際上與這些方法的運(yùn)行時間無關(guān)赖临。

Set 究竟有多快?

雖然運(yùn)行時間可能會有很大差異灾锯,具體取決于所使用的系統(tǒng)兢榨,所提供數(shù)據(jù)的大小以及其他變量,但我希望我的測試結(jié)果能夠讓你真實(shí)地了解Set的速度顺饮。 我將分享三個簡單的測試和我得到的結(jié)果吵聪。

準(zhǔn)備測試

在運(yùn)行任何測試之前,創(chuàng)建一個數(shù)組和一個 Set领突,每個數(shù)組和 Set 都有100萬個元素暖璧。為了簡單起見,我從0開始君旦,一直數(shù)到999999澎办。

let arr = [], set = new Set(), n = 1000000;
for (let i = 0; i < n; i++) {
  arr.push(i);
  set.add(i);
}

測試1:查找元素

我們搜索數(shù)字123123

let result;
console.time('Array'); 
result = arr.indexOf(123123) !== -1; 
console.timeEnd('Array');
console.time('Set'); 
result = set.has(123123); 
console.timeEnd('Set');
  • Array: 0.173ms
  • Set: 0.023ms

Set 速度快了7.54

代碼部署后可能存在的BUG沒法實(shí)時知道,事后為了解決這些BUG金砍,花了大量的時間進(jìn)行l(wèi)og 調(diào)試局蚀,這邊順便給大家推薦一個好用的BUG監(jiān)控工具 Fundebug

測試2:添加元素

console.time('Array'); 
arr.push(n);
console.timeEnd('Array');
console.time('Set'); 
set.add(n);
console.timeEnd('Set');
  • Array: 0.018ms
  • Set: 0.003ms

Set 速度快了6.73

測試3:刪除元素

最后恕稠,刪除一個元素琅绅,由于數(shù)組沒有內(nèi)置方法,首先先創(chuàng)建一個輔助函數(shù):

const deleteFromArr = (arr, item) => {
  let index = arr.indexOf(item);
  return index !== -1 && arr.splice(index, 1);
};

這是測試的代碼:

console.time('Array'); 
deleteFromArr(arr, n);
console.timeEnd('Array');
console.time('Set'); 
set.delete(n);
console.timeEnd('Set');
  • Array: 1.122ms
  • Set: 0.015ms

Set 速度快了74.13

總的來說鹅巍,我們可以看到千扶,使用Set 極大地改善運(yùn)行時間。再來看看一些Set有用的實(shí)際例子骆捧。

案例1:從數(shù)組中刪除重復(fù)的值

如果想快速地從數(shù)組中刪除重復(fù)的值澎羞,可以將其轉(zhuǎn)換為一個 Set。這是迄今為止過濾惟一值最簡潔的方法:

const duplicateCollection = ['A', 'B', 'B', 'C', 'D', 'B', 'C'];
// 將數(shù)組轉(zhuǎn)換為 Set
let uniqueCollection = new Set(duplicateCollection);
console.log(uniqueCollection) // Result: Set(4) {"A", "B", "C", "D"}
// 值保存在數(shù)組中
let uniqueCollection = [...new Set(duplicateCollection)];
console.log(uniqueCollection) // Result: ["A", "B", "C", "D"]

案例2:谷歌面試問題

問題:

給定一個整數(shù)無序數(shù)組和變量 sum敛苇,如果存在數(shù)組中任意兩項(xiàng)和使等于 sum 的值妆绞,則返回true。否則,返回false枫攀。例如括饶,數(shù)組[3,5,1,4]sum = 9,函數(shù)應(yīng)該返回true来涨,因?yàn)?code>4 + 5 = 9图焰。

解答

解決這個問題的一個很好的方法是遍歷數(shù)組,創(chuàng)建 Set保存相對差值蹦掐。

當(dāng)我們遇到3時楞泼,我們可以把6加到Set中, 因?yàn)槲覀冎牢覀冃枰业?code>9的和驰徊。然后,每當(dāng)我們接觸到數(shù)組中的新值時堕阔,我們可以檢查它是否在 Set 中棍厂。當(dāng)遇到5時,在 Set 加上4超陆。最后牺弹,當(dāng)我們最終遇到4時,可以在Set中找到它时呀,就返回true张漂。

const findSum = (arr, val) => {
  let searchValues = new Set();
  searchValues.add(val - arr[0]);
  for (let i = 1, length = arr.length; i < length; i++) {
    let searchVal = val - arr[i];
    if (searchValues.has(arr[i])) {
      return true;
    } else {
      searchValues.add(searchVal);
    }
  };
  return false;
};

簡潔的版本:

const findSum = (arr, sum) =>
  arr.some((set => n => set.has(n) || !set.add(sum - n))(new Set));

因?yàn)?code>Set.prototype.has()的時間復(fù)雜度僅為O(1),所以使用 Set 來代替數(shù)組谨娜,最終使整個解決方案的線性運(yùn)行時為O(N)航攒。

如果使用 Array.prototype.indexOf()Array.prototype.includes(),它們的時間復(fù)雜度都為 O(N)趴梢,則總運(yùn)行時間將為O(N2)漠畜,慢得多!

參考

關(guān)于Fundebug

Fundebug專注于JavaScript坞靶、微信小程序憔狞、微信小游戲、支付寶小程序彰阴、React Native瘾敢、Node.js和Java線上應(yīng)用實(shí)時BUG監(jiān)控。 自從2016年雙十一正式上線尿这,F(xiàn)undebug累計(jì)處理了10億+錯誤事件簇抵,付費(fèi)客戶有陽光保險、核桃編程射众、荔枝FM碟摆、掌門1對1、微脈责球、青團(tuán)社等眾多品牌企業(yè)焦履。歡迎大家免費(fèi)試用拓劝!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末雏逾,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子郑临,更是在濱河造成了極大的恐慌栖博,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件厢洞,死亡現(xiàn)場離奇詭異仇让,居然都是意外死亡典奉,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進(jìn)店門丧叽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來卫玖,“玉大人,你說我怎么就攤上這事踊淳〖偎玻” “怎么了?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵迂尝,是天一觀的道長脱茉。 經(jīng)常有香客問我,道長垄开,這世上最難降的妖魔是什么琴许? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮溉躲,結(jié)果婚禮上榜田,老公的妹妹穿的比我還像新娘。我一直安慰自己签财,他們只是感情好串慰,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著唱蒸,像睡著了一般邦鲫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上神汹,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天庆捺,我揣著相機(jī)與錄音,去河邊找鬼屁魏。 笑死滔以,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的氓拼。 我是一名探鬼主播你画,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼桃漾!你這毒婦竟也來了坏匪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤撬统,失蹤者是張志新(化名)和其女友劉穎适滓,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體恋追,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡凭迹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年罚屋,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嗅绸。...
    茶點(diǎn)故事閱讀 39,745評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡脾猛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鱼鸠,到底是詐尸還是另有隱情尖滚,我是刑警寧澤,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布瞧柔,位于F島的核電站漆弄,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏造锅。R本人自食惡果不足惜撼唾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望哥蔚。 院中可真熱鬧倒谷,春花似錦、人聲如沸糙箍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽深夯。三九已至抖格,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間咕晋,已是汗流浹背雹拄。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留掌呜,地道東北人滓玖。 一個月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像质蕉,于是被迫代替她去往敵國和親势篡。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評論 2 354

推薦閱讀更多精彩內(nèi)容