算法:用Java实现一个带删除功能的布隆过滤器(BloomFilter)

上一篇博客,我实现了一个迷你的布隆过滤器(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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章