上一篇博客,我實現了一個迷你的布隆過濾器(BloomFilter)。今天我再實現一個帶刪除功能的布隆過濾器增強版
布隆過濾器刪除功能的原理,就是原來是通過true和false來判斷,而現在是通過計數來判斷了。add()操作就是往那些哈希值上面+1,刪除就是對應的哈希值-1。當然刪除之前需要判斷,如果有的哈希值都是0了,肯定就不讓刪了。和exists()操作一樣,刪除功能也有可能失效,就是刪除了之後還exists()還能返回true,因爲原來是通過true來判斷,現在是值大於零來判斷了。如果add()的太多,基數太大,則有可能delete()失效。使用的時候需要考慮這個問題(筆者也不太建議使用刪除功能)
接下來是我用Java實現的一個帶刪除功能的布隆過濾器,同樣自定義了五個字符串的哈希函數,用HashMap實現CountMap。相比於mini布隆過濾器,新增了delete()操作,返回是否刪除成功的boolean。其中算法的原理和精髓都在代碼和其間的詳細註釋中:
import java.util.HashMap;
import java.util.Map;
/**
* @author LiYang
* @ClassName BloomFilter
* @Description 布隆過濾器的Java實現類,帶刪除功能
* @date 2019/12/2 15:14
*/
public class BloomFilterEnhance {
//布隆過濾器的哈希表的大小
private static final int BLOOM_HASH_TABLE_SIZE = 1021;
/**
* 布隆過濾器自定義哈希函數1:
* 返回字符串原始的哈希值絕對值
* @param element 輸入字符串
* @return 字符串原始的哈希值絕對值
*/
private static int hashCodeAll(String element) {
return Math.abs(element.hashCode());
}
/**
* 布隆過濾器自定義哈希函數2:
* 返回字符串左半邊的子串哈希值絕對值
* @param element 輸入字符串
* @return 字符串左半邊的子串哈希值絕對值
*/
private static int hashCodeLeft(String element) {
return Math.abs(element.substring(0, element.length() / 2).hashCode());
}
/**
* 布隆過濾器自定義哈希函數3:
* 返回字符串右半邊的子串哈希值絕對值
* @param element 輸入字符串
* @return 字符串右半邊的子串哈希值絕對值
*/
private static int hashCodeRight(String element) {
return Math.abs(element.substring(element.length() / 2).hashCode());
}
/**
* 布隆過濾器自定義哈希函數4:
* 返回字符串奇數位數字符組成的字符串的哈希值絕對值
* @param element 輸入字符串
* @return 字符串奇數位數字符組成的字符串的哈希值絕對值
*/
private static int hashCodeOdd(String element) {
StringBuffer sbuf = new StringBuffer();
//收集奇數位數的字符
for (int i = 0; i < element.length(); i++) {
if ((i + 1) % 2 == 1) {
sbuf.append(element.charAt(i));
}
}
return Math.abs(sbuf.toString().hashCode());
}
/**
* 布隆過濾器自定義哈希函數5:
* 返回字符串偶數位數字符組成的字符串的哈希值絕對值
* @param element 輸入字符串
* @return 字符串偶數位數字符組成的字符串的哈希值絕對值
*/
private static int hashCodeEven(String element) {
StringBuffer sbuf = new StringBuffer();
//收集偶數位數的字符
for (int i = 0; i < element.length(); i++) {
if ((i + 1) % 2 == 0) {
sbuf.append(element.charAt(i));
}
}
return Math.abs(sbuf.toString().hashCode());
}
//布隆過濾器的哈希表(模仿CountMap)
private Map<Integer, Integer> bloomFilterCountMap = new HashMap<>();
/**
* 布隆過濾器的構造方法
*/
public BloomFilterEnhance() {
//初始化布隆過濾器"CountMap"
for (int i = 0; i < BLOOM_HASH_TABLE_SIZE; i++) {
bloomFilterCountMap.put(i, 0);
}
}
/**
* 布隆過濾器:新增操作
* @param element 新增的字符串
*/
public void add(String element) {
//計算五種哈希函數得到的哈希值
int bloomHashCodeAll = hashCodeAll(element) % BLOOM_HASH_TABLE_SIZE;
int bloomHashCodeLeft = hashCodeLeft(element) % BLOOM_HASH_TABLE_SIZE;
int bloomHashCodeRight = hashCodeRight(element) % BLOOM_HASH_TABLE_SIZE;
int bloomHashCodeOdd = hashCodeOdd(element) % BLOOM_HASH_TABLE_SIZE;
int bloomHashCodeEven =hashCodeEven(element) % BLOOM_HASH_TABLE_SIZE;
//加入到布隆過濾器"CountMap"之中
bloomFilterCountMap.put(bloomHashCodeAll, bloomFilterCountMap.get(bloomHashCodeAll) + 1);
bloomFilterCountMap.put(bloomHashCodeLeft, bloomFilterCountMap.get(bloomHashCodeLeft) + 1);
bloomFilterCountMap.put(bloomHashCodeRight, bloomFilterCountMap.get(bloomHashCodeRight) + 1);
bloomFilterCountMap.put(bloomHashCodeOdd, bloomFilterCountMap.get(bloomHashCodeOdd) + 1);
bloomFilterCountMap.put(bloomHashCodeEven, bloomFilterCountMap.get(bloomHashCodeEven) + 1);
}
/**
* 布隆過濾器:查詢是否存在(有一定的誤判率,跟哈希表大小和哈希函數有關)
* @param element 查詢存在與否的字符串
* @return 該字符串是否存在
*/
public boolean exists(String element) {
//計算五種哈希函數得到的哈希值
int bloomHashCodeAll = hashCodeAll(element) % BLOOM_HASH_TABLE_SIZE;
int bloomHashCodeLeft = hashCodeLeft(element) % BLOOM_HASH_TABLE_SIZE;
int bloomHashCodeRight = hashCodeRight(element) % BLOOM_HASH_TABLE_SIZE;
int bloomHashCodeOdd = hashCodeOdd(element) % BLOOM_HASH_TABLE_SIZE;
int bloomHashCodeEven =hashCodeEven(element) % BLOOM_HASH_TABLE_SIZE;
//到布隆過濾器"CountMap"之中驗證
//全部哈希值對應CountMap的值都大於零,則有可能存在
//如果有一個不大於零,則肯定不存在
return bloomFilterCountMap.get(bloomHashCodeAll) > 0
&& bloomFilterCountMap.get(bloomHashCodeLeft) > 0
&& bloomFilterCountMap.get(bloomHashCodeRight) > 0
&& bloomFilterCountMap.get(bloomHashCodeOdd) > 0
&& bloomFilterCountMap.get(bloomHashCodeEven) > 0;
}
/**
* 布隆過濾器:刪除某個元素(必須是原先add()過的元素,否則有一定風險)
* @param element 需要刪除的字符串
* @return 是否刪除成功
*/
public boolean delete(String element) {
//如果需要刪除的element不存在,返回false
if (!exists(element)) {
return false;
}
//計算五種哈希函數得到的哈希值
int bloomHashCodeAll = hashCodeAll(element) % BLOOM_HASH_TABLE_SIZE;
int bloomHashCodeLeft = hashCodeLeft(element) % BLOOM_HASH_TABLE_SIZE;
int bloomHashCodeRight = hashCodeRight(element) % BLOOM_HASH_TABLE_SIZE;
int bloomHashCodeOdd = hashCodeOdd(element) % BLOOM_HASH_TABLE_SIZE;
int bloomHashCodeEven =hashCodeEven(element) % BLOOM_HASH_TABLE_SIZE;
//在布隆過濾器"CountMap"之中刪除該元素
bloomFilterCountMap.put(bloomHashCodeAll, bloomFilterCountMap.get(bloomHashCodeAll) - 1);
bloomFilterCountMap.put(bloomHashCodeLeft, bloomFilterCountMap.get(bloomHashCodeLeft) - 1);
bloomFilterCountMap.put(bloomHashCodeRight, bloomFilterCountMap.get(bloomHashCodeRight) - 1);
bloomFilterCountMap.put(bloomHashCodeOdd, bloomFilterCountMap.get(bloomHashCodeOdd) - 1);
bloomFilterCountMap.put(bloomHashCodeEven, bloomFilterCountMap.get(bloomHashCodeEven) - 1);
//刪除成功
return true;
}
/**
* 運行驗證布隆過濾器
* @param args
*/
public static void main(String[] args) {
//布隆過濾器類
BloomFilterEnhance bloomFilterEnhance = new BloomFilterEnhance();
//先加入一週七天的英文字符
bloomFilterEnhance.add("Monday");
bloomFilterEnhance.add("Tuesday");
bloomFilterEnhance.add("Wednesday");
bloomFilterEnhance.add("Thursday");
bloomFilterEnhance.add("Friday");
bloomFilterEnhance.add("Saturday");
bloomFilterEnhance.add("Sunday");
//運行布隆過濾器,檢查結果
System.out.println("布隆過濾器生效:");
System.out.println("is Friday exists? " + bloomFilterEnhance.exists("Friday"));
System.out.println("is FireChicken exists? " + bloomFilterEnhance.exists("FireChicken"));
System.out.println();
System.out.println("布隆過濾器生效:");
System.out.println("is Saturday exists? " + bloomFilterEnhance.exists("Saturday"));
System.out.println("is Sasurday exists? " + bloomFilterEnhance.exists("Sasurday"));
System.out.println();
//布隆過濾器誤刪
boolean flag = bloomFilterEnhance.delete("Week");
System.out.println("刪除Week(本身不存在),刪除是否成功:" + flag);
System.out.println();
System.out.println("布隆過濾器真刪除:成功");
System.out.println("刪除前,Wednesday是否還在:" + bloomFilterEnhance.exists("Wednesday"));
System.out.println("刪除Wednesday,是否成功:" + bloomFilterEnhance.delete("Wednesday"));
System.out.println("刪除後,Wednesday是否還在:" + bloomFilterEnhance.exists("Wednesday"));
System.out.println();
System.out.println("布隆過濾器真刪除:成功");
System.out.println("刪除前,Tuesday是否還在:" + bloomFilterEnhance.exists("Tuesday"));
System.out.println("刪除Tuesday,是否成功:" + bloomFilterEnhance.delete("Tuesday"));
System.out.println("刪除後,Tuesday是否還在:" + bloomFilterEnhance.exists("Tuesday"));
}
}
運行BloomFilterEnhance類的main方法,增強布隆過濾器的數據結構和算法測試通過!由於刪除功能可能會入參之前未加入的字符串,造成數據變得混亂!所以個人建議還是不要使用刪除功能,還是用之前那個mini的布隆過濾器吧!
布隆過濾器生效:
is Friday exists? true
is FireChicken exists? false
布隆過濾器生效:
is Saturday exists? true
is Sasurday exists? false
刪除Week(本身不存在),刪除是否成功:false
布隆過濾器真刪除:成功
刪除前,Wednesday是否還在:true
刪除Wednesday,是否成功:true
刪除後,Wednesday是否還在:false
布隆過濾器真刪除:成功
刪除前,Tuesday是否還在:true
刪除Tuesday,是否成功:true
刪除後,Tuesday是否還在:false