大數(shù)據(jù)中時(shí)常都會有優(yōu)化,這篇文章要和大家降的就是如何按照特定的條件刪除一個(gè)數(shù)組?
1、如何刪除數(shù)組中的元素
場景:有一個(gè)數(shù)組,需要?jiǎng)h除滿足條件的數(shù)組。
示例:
const arr = [1,2,3,4,5,6,7,8]
刪除小于5的元素,刪除后的元素為
const arr2 = [5, 6, 7, 8]
代碼實(shí)現(xiàn):
const arr = [1,2,3,4,5,6,7,8] for(let i = 0, len = arr.length; i < len; i++) { if(arr[i] < 5) { arr.splice(i, 1) } }
結(jié)果如下
arr = [2, 4, 5, 6, 7, 8
不是我們預(yù)期的結(jié)果
分析原因:刪除操作會使得對應(yīng)索引值位上的元素清空,整個(gè)數(shù)組中的元素向前移動一位,補(bǔ)位的元素會填充到執(zhí)行刪除操作的索引值位置上,移位之后如果不進(jìn)行任何操作繼續(xù)下一個(gè)循環(huán),會導(dǎo)致補(bǔ)位元素跳過遍歷,為了防止這種補(bǔ)位元素跳過遍歷現(xiàn)象,應(yīng)該在刪除操作后將索引值減1,對執(zhí)行刪除操作的索引值位置再進(jìn)行一次遍歷 。
改進(jìn):
const arr = [1,2,3,4,5,6,7,8] for(let i = 0, len = arr.length; i < len; i++) { if(arr[i] < 5) { arr.splice(i, 1) i--; } } // arr = [5, 6, 7, 8] 符合預(yù)期
這個(gè)是做了正序循環(huán)刪除,也可以使用倒序循環(huán)刪除:
const arr = [1,2,3,4,5,6,7,8] for(let i = arr.length - 1; i >= 0; i--) { if(arr[i] < 5) { arr.splice(i, 1) } } // arr = [5, 6, 7, 8] 符合預(yù)期
2、當(dāng)消息有10000,000條優(yōu)化又如何?
場景
彈幕消息發(fā)送場景模擬(偽直播形式,沒有進(jìn)度條):假設(shè)我們有10000,000條消息,根據(jù)視頻播放的進(jìn)度展示對應(yīng)的消息,不展示歷史消息。
常規(guī)思路:
循環(huán)遍歷整個(gè)消息列表,時(shí)刻監(jiān)聽視頻播放的進(jìn)度,根據(jù)視頻播放的時(shí)間戳和消息發(fā)送的時(shí)間戳先相等,然后展示消息,依次循環(huán)。
產(chǎn)生的問題
每次視頻進(jìn)度變化都會循環(huán)整個(gè)消息列表,當(dāng)循環(huán)還沒完成,下一個(gè)播放進(jìn)度監(jiān)聽觸發(fā)了,又開始下一個(gè)循環(huán),這樣就會造成性能的損耗。
優(yōu)化策略
我們從上面的分析可以看出,當(dāng)消息發(fā)送了一條,就可以從原始數(shù)據(jù)刪除這條消息,然后跳出循環(huán),這樣循環(huán)的次數(shù)始終控制在幾次(或者幾十次)的范圍(有可能同一個(gè)時(shí)間段同時(shí)有幾條消息甚至幾十條消息)等下一個(gè)播放進(jìn)度監(jiān)聽觸發(fā),開始循環(huán)原始數(shù)據(jù),這是之前以后發(fā)送過得數(shù)據(jù)刪除了,就不會再循環(huán)刪除過的數(shù)據(jù),始終循環(huán)需要發(fā)送的那幾條,找到了就直接跳出循環(huán)。
代碼實(shí)現(xiàn)
// 模擬原始消息列表, const newList = new Array(10000000).fill(1).map((item, index) => { return { time: (index + 1) * 1000, // 消息發(fā)送的時(shí)間,一秒一個(gè) content: `這是第${index + 1}s發(fā)送的消息` // 消息發(fā)送的內(nèi)容 } }) // 發(fā)送的消息列表 const sendList = []; function getMessage(time) { let j = 0; // 循環(huán)次數(shù) for(let i = 0, len = newList.length; i < len; i++) { const item = newList[i]; j++; // 這里的time如果不是1000、2000,而是1234、1214這種,就需要取一個(gè)浮動范圍 // 我這里就是簡單用了定時(shí)器,所以比較簡單 if(item.time === time) { sendList.push(newList[i]) newList.splice(i, 1) i--; } else if(sendList.length > 0) { break; } } console.log('播放進(jìn)度', time) console.log('循環(huán)的次數(shù)', j); console.log('接收的消息的長度', sendList.length, sendList); console.log('原始消息的長度', newList.length); } let time = 0; // 定時(shí)器,1s觸發(fā)一次 setInterval(() => { time += 1000; getMessage(time); }, 1000)
// 消息格式 newList = [ {time: 1000, content: '這是第1s發(fā)送的消息'}, {time: 2000, content: '這是第2s發(fā)送的消息'}, ... ]
效果展示
小結(jié)
上述優(yōu)化總的來說就是發(fā)送過的消息刪除,下次少循環(huán)。當(dāng)找到滿足條件的數(shù)據(jù),直接跳出循環(huán),后面的數(shù)據(jù)不再循環(huán)。
缺點(diǎn)就是使用slice也會消耗性能,不可取,并且操作繁瑣。
下面來說說游標(biāo)法代替splice
我們這里不再使用slice的方案,設(shè)置一個(gè)游標(biāo),記錄循環(huán)的初始位置,下次循環(huán)直接從游標(biāo)記錄的位置開始循環(huán),然后滿足查找的條件就break,這樣既不破壞原來的數(shù)組,也能有效的減少循環(huán)的次數(shù)。
let index = 0, sendList =[]; function getMessage(time) { for(let i = 0, len = newList.length; i < len; i++) { const item = newList[i]; // 這里的time如果不是1000、2000,而是1234、1214這種,就需要取一個(gè)浮動范圍 // 我這里就是簡單用了定時(shí)器,所以比較簡單 if(item.time === time) { index = i; sendList.push(newList[i]) } else if(sendList.length > 0) { // 這里的查詢結(jié)束條件為,對應(yīng)的時(shí)間范圍之外沒有消息了,并且需要發(fā)送的消息列表有消息,才break // 這里的結(jié)束條件想不到什么更好的方案了 break; } } }
上面我們只對視頻播放的時(shí)候做了優(yōu)化,如果下次用戶進(jìn)來進(jìn)度直接接近尾聲了,這時(shí)候首次查找尾部消息的時(shí)候,就需要把前面所有的消息都循環(huán)一遍,所以還需要繼續(xù)優(yōu)化。
二分查找
當(dāng)首次加載的時(shí)候,采用二分法查找到消息開始的位置,當(dāng)視頻播放的時(shí)候再根據(jù)查找到的index去循環(huán)消息體。
function binarySearch(arr, time) { let upperBound = arr.length - 1; // 記錄長度 let lowerBound = 0; // 記錄上次二分的位置 let mid; // 切半分的位置 小于或等于 1就停止循環(huán)了 while (lowerBound <= upperBound) { // (當(dāng)前總長度 + 當(dāng)前中間點(diǎn)位置長度) / 2 = 實(shí)際的中間點(diǎn)位置 mid = Math.floor((upperBound + lowerBound) / 2); const item = arr[mid]; const maxTime = time +500; const minTime = time + 500; // 當(dāng)輸入的值大于中間值時(shí),向后移動一位 if (time > maxTime) { lowerBound = mid + 1; } else if (time < minTime) { // 當(dāng)輸入值小于中間值時(shí),向前移動一位 upperBound = mid - 1; } else { return mid; // 找到指定數(shù)據(jù)位置 } } return -1; }
function findIndex(startPlayTime: number) { const searchIndex = binarySearch(this.messageList, time); // 賦值索引,用于快速發(fā)送消息 if (searchIndex !== -1) { index = searchIndex; } }
總結(jié)
方法、方式都是在逐漸的優(yōu)化,從最開始的splice方法,然后到后面的游標(biāo)法和二分法,做了逐漸的優(yōu)化。
上述內(nèi)容對于大家知識上可能不是非常重要,上面講的也是我們平時(shí)在寫代碼的時(shí)候,在日常寫代碼中也是在不斷的優(yōu)化,也是一種negligible的提升,。例如,找leader去給你review代碼,就是讓我們在日常的工作業(yè)務(wù)中去不斷的提升自己的能力,讓自己成長。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/127570.html
摘要:考慮到只適合在單位時(shí)間內(nèi)繪制少量彈幕,這對于我們播放器來說明顯是不合需求的。比如說直播播放器模式和視頻播放器模式的優(yōu)化細(xì)節(jié)又有點(diǎn)不一樣,還有高級彈幕的處理圖片,圖形等。。 前言 大家年前好,馬上就要元旦了,在很久沒有寫文章之后,想到這篇文章將會成為本人今年的絕響也是有點(diǎn)蛋疼。不過也好,畢竟本人算不得什么勤快的生物,而且比起那些大神來說也差遠(yuǎn)了,就作為自己工作半年后的一次沉淀算了。 文中...
摘要:考慮到只適合在單位時(shí)間內(nèi)繪制少量彈幕,這對于我們播放器來說明顯是不合需求的。比如說直播播放器模式和視頻播放器模式的優(yōu)化細(xì)節(jié)又有點(diǎn)不一樣,還有高級彈幕的處理圖片,圖形等。。 前言 大家年前好,馬上就要元旦了,在很久沒有寫文章之后,想到這篇文章將會成為本人今年的絕響也是有點(diǎn)蛋疼。不過也好,畢竟本人算不得什么勤快的生物,而且比起那些大神來說也差遠(yuǎn)了,就作為自己工作半年后的一次沉淀算了。 文中...
閱讀 561·2023-03-27 18:33
閱讀 750·2023-03-26 17:27
閱讀 647·2023-03-26 17:14
閱讀 603·2023-03-17 21:13
閱讀 537·2023-03-17 08:28
閱讀 1823·2023-02-27 22:32
閱讀 1315·2023-02-27 22:27
閱讀 2199·2023-01-20 08:28