上一篇博客,我实现了一个迷你的布隆过滤器(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