最近一直遠端辦公,所以經常會從各種群郵件,釘釘等軟體上收到一些重複的檔案,突然發現原本不大的筆記本硬碟也會經常被這些檔案所佔據,而針對windows的重複檔案清理軟體如duplicate cleaner等筆者親測也會帶有一些**式的安裝,而其實這個需求沒有那麼難以解決,簡單來說只需要建立 乙個以檔名為索引,檔案大小為鍵值的雜湊表,然後遍歷所有檔案就能完成,在看過了所有典型的雜湊表之後筆者發現go語言的雜湊表非常優秀,接下來就帶大家一起來認識一下其中的奧秘,接下來筆者還會帶大家一起來用go語言實現乙個檔案去重的小工具。
在現代的系統研發尤其是
web類系統中一般都使用負載均衡裝置加分布式應用伺服器的方式來完成,而同一客戶的
session
一般都要保證始終發給一台應用伺服器處理,負載裝置每次需要通過
session id
迅速定位到伺服器,那麼
session id
和應用伺服器的
ip位址就是
map的典型應用。 go
語言中的
map原始碼非常值得一讀下面給大家做一下簡要介紹
1.map
的資料結構
可以看到
hmap
是map
的基礎結構其中,其中記錄了
map的元素的數量,和
bucket
的數量,以及
map現在是否正在遷移等狀態資訊詳見以下注釋說明。
type hmap struct
// 在桶溢位的時候會用到extra
map建立時會初始化乙個
hmap
結構體,在訪問
map中的
pair
時,先計算
key的雜湊值,其中雜湊的值的低
8位定位到具體的桶(
bucket
),通過高
8位在桶內定位到具體的位置。
2.map
的建立
我們再來看
makemap
函式,如果首先看元素的個數,計算乙個
b值,如果
map只需要乙個桶(
bucket
)就可以直接建立在棧上,而不是在堆上如果
h.buckets
其指向的桶(
bucket
)可以作為第乙個桶(
bucket
)來使用。詳見以下**注釋
func makemap(t *maptype, hint int, h *hmap) *hmap ); sz != 8+5*sys.ptrsize
if hint < 0 || hint > int(maxslicecap(t.bucket.size))
// 初始化 hmap
if h == nil
h.hash0 = fastrand()
// 按照提供的元素個數,找乙個可以放得下這麼多元素的 b 值
b := uint8(0)
for overloadfactor(hint, b)
h.b = b
// 分配初始的 hash table
// 如果 b == 0,buckets 欄位會由 mapassign 來 lazily 分配
// 因為如果 hint 很大的話,對這部分記憶體歸零會花比較長時間
if h.b != 0
}return h
}
3.map
中元素的訪問
再來看訪問元素的函式
mapaccess
,可以看到該函式會根據不同的
key型別來選擇不同的雜湊演算法,而且從這個函式也可以看到,
map並不能保證併發安全的,因為在對外訪問的時候
map還可能正在擴容,一旦在擴容時發生併發訪問可能會有潛在的問題,不過在go的
sync
包下也有乙個
map的實現,這個
map是協程安全的。
mapaccess
**及注釋如下
func mapaccess(t *maptype, h *hmap, key unsafe.pointer) (unsafe.pointer, bool)
if h.flags&hashwriting != 0
alg := t.key.alg
// 不同型別的 key,所用的 hash 演算法是不一樣的
// 具體可以參考 algarray
hash := alg.hash(key, uintptr(h.hash0))
// 如果 b = 3,那麼結果用二進位制表示就是 111
// 如果 b = 4,那麼結果用二進位制表示就是 1111
m := bucketmask(h.b)
// 按位 &,可以 select 出對應的 bucket
b := (*bmap)(unsafe.pointer(uintptr(h.buckets) + (hash&m)*uintptr(t.bucketsize)))
// 會用到 h.oldbuckets 時,說明 map 發生了擴容
// 這時候,新的 buckets 裡可能還沒有老的內容
// 所以一定要在老的裡面找,否則有可能發生「消失」的詭異現象
if c := h.oldbuckets; c != nil
oldb := (*bmap)(unsafe.pointer(uintptr(c) + (hash&m)*uintptr(t.bucketsize)))
if !evacuated(oldb)
}// tophash 取其高 8bit 的值
top := tophash(hash)
for ; b != nil; b = b.overflow(t)
k := add(unsafe.pointer(b), dataoffset+i*uintptr(t.keysize))
if t.indirectkey
if alg.equal(key, k)
return v, true}}
}// 所有 bucket 都沒有找到,返回零值和 false
return unsafe.pointer(&zeroval[0]), false
}
3.通過分析源**筆者有以下結論 首先
map底層是
hash
實現,資料結構為
hash
陣列+
桶每個桶儲存最多8個
key-value
對,如果遇溢位,暨乙個桶被分配到的元素不止
8個,則加入溢位桶的操作。其次
map查詢是通過
key的
hash
值的低8
位定位到桶,再從桶中定位到得到具體的
key。而且
go map
不支援在併發。插入、刪除、搬遷等操作可能會使
map進行遷移,這時併發訪問會產生
panic。
從時間複雜度上分析,
map正常的時間複雜度是
o(1)
,如果所有資料都被集中到乙個桶及溢位桶內,那麼時間複雜度退化為
o(n)
一文說透https中的s是什麼?
一 http 與 https 有哪些區別?1.http 是超文字傳輸協議,資訊是明文傳輸,存在安全風險。https 是在 tcp 和網路層之間加入了 ssl tls 安全協議,也就是安全套接字層,使得報文能夠加密傳輸。2.http 連線建立相對簡單,tcp 三次握手建立之後便可進行 http 的報文...
一文說透WordPress的自定義文章型別
丘壑部落格 從2004年的1.0版本算起,wordpress在14年間已經迭代開發到了5.x版。如果說這中間哪個版本是乙個質的提公升的話,那應該算是2010年發布的代號為thelonious 的3.0版。這個版本發布了很多重要的功能,比如多站點 主題api等等,其中乙個就是 custom post ...
一文說透WordPress的自定義文章型別
丘壑部落格 從2004年的1.0版本算起,wordpress在14年間已經迭代開發到了5.x版。如果說這中間哪個版本是乙個質的提公升的話,那應該算是2010年發布的代號為thelonious 的3.0版。這個版本發布了很多重要的功能,比如多站點 主題api等等,其中乙個就是 custom post ...