Java中的布隆過濾器你真的懂了嗎
什么是布隆過濾器
布隆過濾器(Bloom Filter)是一種空間效率非常高的隨機(jī)數(shù)據(jù)結(jié)構(gòu),它利用位數(shù)組(BitSet)表示一個(gè)集合,并通過一定數(shù)量的哈希函數(shù)將元素映射為位數(shù)組中的位置,用于檢查一個(gè)元素是否屬于這個(gè)集合。
實(shí)現(xiàn)的核心思想
對(duì)于一個(gè)元素,通過多個(gè)哈希函數(shù)生成多個(gè)哈希值,將對(duì)應(yīng)的位在位數(shù)組中設(shè)為 1,若多個(gè)哈希值對(duì)應(yīng)的位都為 1,則認(rèn)為該元素可能在集合中;若至少有一個(gè)哈希值對(duì)應(yīng)的位為 0,則該元素一定不在集合中。這種方法可以在較小的空間中實(shí)現(xiàn)高效的查找,但可能存在誤判率(false positive)。
怎么理解
一個(gè)典型的布隆過濾器包含三個(gè)參數(shù): 位數(shù)組的大?。创鎯?chǔ)元素的個(gè)數(shù)); 哈希函數(shù)的個(gè)數(shù); 填充因子(即誤判率),即將元素?cái)?shù)量與位數(shù)組大小的比值。
如上圖所示: 布隆過濾器的基本操作流程,包括初始化位數(shù)組和哈希函數(shù)、插入元素、檢查元素是否在集合中等。其中,每個(gè)元素都會(huì)被多個(gè)哈希函數(shù)映射到位數(shù)組中的多個(gè)位置,而在檢查元素是否在集合中時(shí),需要確保所有對(duì)應(yīng)的位都被設(shè)置為 1,才會(huì)認(rèn)為該元素可能在集合中。
典型應(yīng)用場(chǎng)景
垃圾郵件過濾: 將所有的黑名單郵件對(duì)應(yīng)的哈希值在布隆過濾器中對(duì)應(yīng)的位置設(shè)為 1,對(duì)于每一封新郵件,將其哈希值在布隆過濾器中對(duì)應(yīng)的位置檢查是否都為 1,若是,則認(rèn)為該郵件是垃圾郵件,否則可能是正常郵件;
URL 去重: 將已經(jīng)抓取的 URL 對(duì)應(yīng)的哈希值在布隆過濾器中對(duì)應(yīng)的位置設(shè)為 1,對(duì)于每一條新的 URL,將其哈希值在布隆過濾器中對(duì)應(yīng)的位置檢查是否都為 1,若是,則認(rèn)為該 URL 已經(jīng)抓取過,否則需要進(jìn)行抓?。?/p>
緩存擊穿: 將緩存中存在的所有數(shù)據(jù)對(duì)應(yīng)的哈希值在布隆過濾器中對(duì)應(yīng)的位置設(shè)為 1,對(duì)于每一個(gè)查詢的鍵值,將其哈希值在布隆過濾器中對(duì)應(yīng)的位置檢查是否都為 1,若是,則認(rèn)為該鍵值存在于緩存中,否則需要從數(shù)據(jù)庫中查詢并將其添加到緩存中。
需要注意的是,布隆過濾器的誤判率會(huì)隨著位數(shù)組大小的增加而減小,但同時(shí)也會(huì)增加內(nèi)存開銷和計(jì)算時(shí)間。 為了方便理解布隆過濾器,下面用java代碼實(shí)現(xiàn)一個(gè)簡(jiǎn)單的布隆過濾器:
import java.util.BitSet; import java.util.Random; public class BloomFilter { ??private BitSet bitSet; ??????????// 位集,用于存儲(chǔ)哈希值 ??private int bitSetSize; ????????// 位集大小 ??private int numHashFunctions; ??// 哈希函數(shù)數(shù)量 ??private Random random; ?????????// 隨機(jī)數(shù)生成器 ??// 構(gòu)造函數(shù),根據(jù)期望元素?cái)?shù)量和錯(cuò)誤率計(jì)算位集大小和哈希函數(shù)數(shù)量 ??public BloomFilter(int expectedNumItems, double falsePositiveRate) { ????this.bitSetSize = optimalBitSetSize(expectedNumItems, falsePositiveRate); ????this.numHashFunctions = optimalNumHashFunctions(expectedNumItems, bitSetSize); ????this.bitSet = new BitSet(bitSetSize); ????this.random = new Random(); ??} ??// 根據(jù)期望元素?cái)?shù)量和錯(cuò)誤率計(jì)算最佳位集大小 ??private int optimalBitSetSize(int expectedNumItems, double falsePositiveRate) { ????int bitSetSize = (int) Math.ceil(expectedNumItems * (-Math.log(falsePositiveRate) / Math.pow(Math.log(2), 2))); ????return bitSetSize; ??} ??// 根據(jù)期望元素?cái)?shù)量和位集大小計(jì)算最佳哈希函數(shù)數(shù)量 ??private int optimalNumHashFunctions(int expectedNumItems, int bitSetSize) { ????int numHashFunctions = (int) Math.ceil((bitSetSize / expectedNumItems) * Math.log(2)); ????return numHashFunctions; ??} ??// 添加元素到布隆過濾器中 ??public void add(String item) { ????// 計(jì)算哈希值 ????int[] hashes = createHashes(item.getBytes(), numHashFunctions); ????// 將哈希值對(duì)應(yīng)的位設(shè)置為 true ????for (int hash : hashes) { ??????bitSet.set(Math.abs(hash % bitSetSize), true); ????} ??} ??// 檢查元素是否存在于布隆過濾器中 ??public boolean contains(String item) { ????// 計(jì)算哈希值 ????int[] hashes = createHashes(item.getBytes(), numHashFunctions); ????// 檢查哈希值對(duì)應(yīng)的位是否都為 true ????for (int hash : hashes) { ??????if (!bitSet.get(Math.abs(hash % bitSetSize))) { ????????return false; ??????} ????} ????return true; ??} ??// 計(jì)算給定數(shù)據(jù)的哈希值 ??private int[] createHashes(byte[] data, int numHashes) { ????int[] hashes = new int[numHashes]; ????int hash1 = Math.abs(random.nextInt()); ????int hash2 = Math.abs(random.nextInt()); ????for (int i = 0; i < numHashes; i++) { ??????// 使用兩個(gè)隨機(jī)哈希函數(shù)計(jì)算哈希值 ??????hashes[i] = Math.abs((hash1 * i) + (hash2 * i) + i) % data.length; ????} ????return hashes; ??} }
以上就是Java中的布隆過濾器你真的懂了嗎的詳細(xì)內(nèi)容,更多關(guān)于Java布隆過濾器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring MVC傳遞接收參數(shù)方式小結(jié)
大家在開發(fā)中經(jīng)常會(huì)用到Spring MVC Controller來接收請(qǐng)求參數(shù),主要常用的接收方式就是通過實(shí)體對(duì)象以及形參等方式、有些用于GET請(qǐng)求,有些用于POST請(qǐng)求,有些用于兩者,下面介紹幾種常見的Spring MVC傳遞接收參數(shù)的方式2021-11-11重寫equals的同時(shí)為何要重寫hashCode?
這篇文章主要給大家介紹了關(guān)于重寫equals的同時(shí)為何要重寫hashCode的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01偵聽消息隊(duì)列的Message Listener類示例詳解
Spring AMQP 是基于 Spring 框架的AMQP消息解決方案,提供模板化的發(fā)送和接收消息的抽象層,提供基于消息驅(qū)動(dòng)的 POJO的消息監(jiān)聽等,簡(jiǎn)化了我們對(duì)于RabbitMQ相關(guān)程序的開發(fā),本文給大家介紹偵聽消息隊(duì)列的Message Listener類,感興趣的朋友一起看看吧2023-12-12SWT(JFace)體驗(yàn)之FormLayout布局
SWT(JFace)體驗(yàn)之FormLayout布局示例代碼。2009-06-06詳解快速排序算法中的區(qū)間劃分法及Java實(shí)現(xiàn)示例
這篇文章主要介紹了詳解快速排序算法中的區(qū)間劃分法及Java實(shí)現(xiàn)示例,文中分別介紹了快排時(shí)兩種區(qū)間劃分的思路,需要的朋友可以參考下2016-04-04MyBatis基于pagehelper實(shí)現(xiàn)分頁原理及代碼實(shí)例
這篇文章主要介紹了MyBatis基于pagehelper實(shí)現(xiàn)分頁原理及代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06