本文目錄一覽:
- 1、【golang】海量數據去重-布隆過濾器
- 2、golang elasticsearch指標聚合(metrics) — 2022-04-02
- 3、golang 數組去重去空怎麼實現比較高效
- 4、為什麼很多程序員不用switch,而是大量的if……else if?
【golang】海量數據去重-布隆過濾器
在做域名爆破中,遇到了把一個300G的子域名json文件進行去重,一開始是考慮使用字典進行去重,但是數據量大了,會造成內存泄露。看網上資料介紹了一種方案,就是使用布隆過濾器。
布隆過濾器是一種數據結構,概率型數據結構,特定是高效插入和查詢,可以用來告訴你「某一值一定不存在或者kennel存在」。
相比於傳統的map、set等數據結構,佔用空間更少,但其返回結果是概率型的,不確定。
布隆過濾器內部維護一個bitArray(位數組),開始所有數據為0,當一個元素過來時,能過多個哈希函數(hash1、hash2、hash3)計算不同的hash值,並通過hash值找到bitArray的下標,將裡面的值改為由0變為1。布隆過濾器有一個誤判率,誤判率越低,數組越長,所在空間越大,誤判率越高,數組越小,所佔空間越小。
這裡貼上一個技術大牛的博客地址,裡面對布隆過濾器用法以及在redis裡面處理緩存穿透問題的詳細介紹。
golang elasticsearch指標聚合(metrics) — 2022-04-02
ES指標聚合,就是類似SQL的統計函數,指標聚合可以單獨使用,也可以跟桶聚合一起使用,下面介紹golang如何使用ES的指標聚合。
不了解ES指標聚合相關知識,先看一下 Elasticsearch 指標聚合教程
值聚合,主要用於統計文檔總數,類似SQL的count函數。
基數聚合,也是用於統計文檔的總數,跟Value Count的區別是,基數聚合會去重,不會統計重複的值,類似SQL的count(DISTINCT 欄位)用法。
求平均值
求和計算
求最大值
求最小值
golang 數組去重去空怎麼實現比較高效
一種高效演算法的大概思想:
設置一個循環用來遍曆數組,一個慢一步,,一個快一步,當到了數組末尾或者快的與慢的下標對應的元素相等就退出,,退出循環後再檢測「快的與慢的下標對應的元素相等」這一條件,如果是就說明有重複元素。否則沒有。
為什麼很多程序員不用switch,而是大量的if……else if?
我個人覺得switch其實非常多餘。
1 大部分場景,都是2到3個可能分支,用個if else就可以了,除非有4 個以上分支,太多else顯得不好看,才考慮用switch.
2 switch限制多。switch必須是常量變數。if後面可以寫任意表達式。
3用法複雜,case後面要麼break,要麼return,要是不寫,居然還會繼續執行剩下的分支,對於新手來說分分鐘掉坑。
4 寫法上其實也不比if else優雅簡潔,switch xxx case xxxx ….
所以,switch徒增複雜性,真的不怎麼實用。
如果有10000種switch的可能性,有1000000個值需要被處理,怕是你們說的這些個switch的好處就完全消失了,預期平均每次要比較5000次,1000000個值,總計要比較50億次,不知道你們的CPU是啥主頻能扛得住這個計算量,針對這種情況的終極武器還是hash,根據不同的語言,hash的value可以是匿名函數,可以是介面的不同實現,用hash來快速確定處理演算法,而不是switch
答案:主要因為switch不適合業務系統的實際複雜需求,業務不斷的變更迭代,一更改需求,條件的複雜度高了,switch無力處理。
switch優點
那麼什麼時候適合switch,它的場景是:基於單一變數的值(如枚舉),這樣的可讀性比if條件更清晰。
switch缺點
從上面的場景來看,實在太局限,我來簡單說一下它的一些缺點吧:
1. 現實的業務場景很複雜,條件不單一,一旦需求變更,維護代碼相當崩潰。
2. switch經常忘記寫break,估計很多人一不小心就忘記寫了。如果你看過google的代碼規範,你會發現,Google對switch的要求非常多。
switch的封裝才更靈活
其實switch有人還在用也有一部分是 歷史 原因,但是隨著 科技 的發展,原有的設計以及落後了。
有些編程語言,如Python都沒有switch這種語法。當然也有部分新語言Golang和Kotlin還是繼承下來,但是又把switch包裝了一下,去掉了令人誤會的語法,這才讓switch變得靈活起來了。 如果不封裝,很難用。
IF語句的好處
通過上面描述的缺點也就是if語句更靈活的地方,根據業務進行邏輯條件編寫,可維護性高。同時只要寫的代碼質量高,可讀性也就會更高。
建議
現實的業務實際是很複雜的,我也不建議一定要用大量的if……else if,而是應該儘早返回來減少嵌套,這樣增加了可讀性以及降低維護的成本。
從C/ C++來看,當分支較多且switch要比較的值是連續的話,執行速度遠遠遠遠快於if,因為switch是直接跳到目標代碼執行的,而if則需要執行很多條語句,慢的不是一點點,一般編譯器會根據分支數量和比較的值是否連續生成不同彙編代碼,如果編譯器判定不能提升速度的話,switch生成的彙編代碼和if是一模一樣的沒有任何區別。
至於很多人不用switch我覺得可能是:
1.為了方便寫代碼,思維習慣隨手就用if寫了;
2.可能根本就不懂為什麼要用switch吧。
作為程序員來說,我更喜歡switch的結構,更直觀更容易找到相應的代碼塊。不過為什麼很多程序員不用Switch,而是使用大量的if…else if的結構,甚至像Python已經不支持原生Switch語法了?
這個原因很簡單,因為switch語法結構最後編譯還是通過if…else if來完成代碼的,所以從效率角度來說和if…else if一樣的。但是switch對比條件比較單一,絕大多數支持switch的編程語言都支持等於比較,也就是說變數只能等於case中的條件才會執行代碼塊。但是現實情況中,對比條件絕大多數比單一等於運算要複雜得多,因此很多程序員就直接使用if…else if。但是if…else if的結構,後期維護起來會比較不清晰,畢竟沒有Case…Break那麼直觀。但是添加一些註解應該還是能解決這個問題的。
所以,我現在能使用Switch的時候還是會使用switch,畢竟後期代碼維護起來方便點。不過更多時候還是用if…else if。
送大家以下java學習資料
我曾經接手過一份代碼,遇到過一個三十幾個if else套if else的模塊。
心理罵罵咧咧誰他喵寫的這玩意,然後開始review 歷史 。
大致情況是這樣的:第一個程序員寫下這段代碼時,只有兩個if else;後來開始逐漸加需求,先是一個、兩個,隨後量變引起質變,於是邏輯分支快速擴張。
這個時候已經沒有人願意去重構成switch或是其他什麼設計模式了,畢竟複雜度擺在那裡,萬一崩了還得背鍋。
三四個程序員接手這段代碼之後,就變成我現在這種局面了。
第一個程序員絕對沒有料到這麼簡單的邏輯在之後會變成這麼複雜的模塊,甚至在增添第一第二條else時,也只是很隨意的加上。
所以我覺得,這個鍋絕對是是甲方的,讓他娘的隨便改需求。
這麼一想心裡就好受多了,編程嘛,最重要的是要看的開。
於是我又增加了兩條else,測試,提交,下班。
有時候真的不是我們不想寫好代碼,是不能寫好代碼。寫著寫著需求砍了、需求變了,什麼設計模式都不頂用,最終還是怎樣快怎樣方便怎樣來,因為根本沒人知道這段代碼還能不能活的過下一段需求變動。
有的人肯定要說怎麼不訂合同。合同肯定是有的,但是明明白紙黑字寫的合同,該改還是得改,畢竟你要是不同意甲方那些「微小的變動」,以後還做不做了?!金主真能去得罪?
還是要學會得過且過,跟什麼過不去也不能跟自己過不去,糟糕的代碼忍一忍就完了:代碼能跑、頭髮不少,對我們這些打工的人而言比什麼都重要。
現實工作絕不是課本中的理想狀態,會有無數的突發情況在等著你。你定義了半天觀察者、備忘錄,第二天這部分需求被砍了;寫了半天介面,抽象類,忽然下午告訴你要加個十萬八千里打不著邊的啥東西,於是又開始加適配器,等你加完了告訴你又砍了。甚至有次半夜被喊起來改代碼,等改完了發現需求被撤回了,氣的我直接請了兩天假調整心情。
設計模式和大的框架絕對是一個項目中非常重要的東西,但不是絕對重要的;一個好的PM團隊,在某種意義上,才真正決定了這個項目的代碼質量。[1]
請用5秒鐘的時間查看下面的代碼是否存在bug。
OK,熟練的程序猿應該已經發現Bug所在了,在第8行和第10行下面我沒有添加關鍵字break; 這就導致這段代碼的行為邏輯與我的設計初衷不符了。
缺點一. 語法正確,邏輯錯誤
這就是第一個理由為什麼程序猿很少使用switch來做條件判斷,對於新手來說忘記寫break實在是再普通不過了,就算是老猿忘記寫也是時有發生的事情,而這個語法錯誤在諸多的語法檢查器上沒有辦法檢查出來的,因為從語法角度來說是正確的!可是代碼的處理邏輯卻是錯誤的!用if來重寫這段代碼的話,就不會發生這種錯誤。
上面的代碼為了保證正確我添加了else做一個邏輯上的保證,其實如果不寫else,這段代碼也不會發生邏輯錯誤,而且一旦我忘記寫花括弧的時候,語法編譯器是會提示我添加的,甚至可以使用eslint這種的工具強制我使用花括弧,這樣就不會犯語法錯誤了,一旦出現bug,那麼肯定是我邏輯上的問題了。
缺點二 .死板的語法
switch儘管對於break很寬容,但是對判斷條件很嚴苛,case後面只能跟常量,如果你用C編寫的話,甚至只能用int類型作為判斷條件。對於我們這麼瀟洒自如的程序猿來說,這種限制實在是太麻煩了,用if的話,別說是常量了,我用函數都可以,真正做到方便快捷。
缺點三 .需要子函數來處理分支
這個缺點跟缺點一有關,為了防止漏寫break,因此建議把分支處理方法獨立成一個子函數來處理,這樣在閱讀代碼的時候就會減少忘記寫break帶來的bug,那麼用if來寫的話,我想怎麼寫就怎麼寫,非常隨意自由,但是這也導致了代碼的可讀性大大降低。
switch的優點
既然switch有這麼嚴重的缺點,那怎麼在所有語言中依然會存在呢?那就說下switch的優點吧,它的優點也剛好是它的缺點。
在很久很久以前,那時候的電腦性能還不如一台小霸學習機的時候,聰明的計算機科學家為了提高計算機的處理速度,將一些邏輯分支處理方法簡化了一下,把一些需要做邏輯判斷的操作給固定死,然後只要查表一樣一個一個對一下就能做出相應的反應了。
比如說a=0的判斷,switch和if在cpu上面的處理方式是不一樣的,switch是在編譯階段將子函數的地址和判斷條件綁定了,只要直接將a的直接映射到子函數地址去執行就可以了,但是if處理起來就不一樣了。
它首先要把a的值放到CPU的寄存器中,然後要把比較的值放到CPU的另一個寄存器中,然後做減法,然後根據計算結果跳轉到子函數去執行,這樣一來就要多出3步的操作了,如果邏輯判斷多的話,那麼將會比switch多處許多倍的操作,儘管寄存器操作的速度很快,但是對於當時的學習機來說,這點速度根本不夠用啊。
那還有一個問題,為什麼要使用break來做一個判斷結束呢?這不是很容易造成語法錯誤了?那就要說到子函數的問題上了。
在早起的電腦代碼中是沒有子函數的概念的,那時候都是用goto隨意跳轉的,你想去第10行代碼,很簡單goto 10就可以了。這種編程思維在C的早期階段還是一直受到影響的,因此早期的C也沒有子函數,都是一堆邏輯處理混亂在一起,goto滿天飛,所以那時候你沒有一個最強大腦是寫不了程序的。那為了告訴程序我這裡條件判斷處理結束,就添加了break作為終止符號。後來慢慢的有了子程序,有了更好的編程規範,才一步一步的將寫代碼淪落到體力勞動。
後來發展的新語言為了標榜自己的血統,多少都要參考下C,然後就把switch這種詭異的語法也繼承下來了。但是也不是所有的語言都照搬,比如Google發明的新語言golang和kotlin就又把switch包裝了一下,去掉了令人誤會的語法,又讓switch變得靈活起來了,對了,在代碼重構的時候,還是用switch把,這樣看起來的確代碼更簡潔哦![2]
switch只能用於簡單判斷,不支持表達式。
沒有if else 使用方便。
不是盡量別用,而是不合適沒法用,合適得時候該用還是用。
比如說,變數i要求大於10,小於20,一條if(i10i
原創文章,作者:RKYV,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/131256.html