1. 程式人生 > >淺談BloomFilter【下】用Java實現BloomFilter

淺談BloomFilter【下】用Java實現BloomFilter

通過前一篇文章的學習,對於 BloomFilter 的概念和原理,以及誤報率等計算方法都一個理性的認識了。在這裡,我們將用 Java'實現一個簡單的 BloomFilter 。

package pri.xiaoye.day1029;

import java.io.Serializable;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.BitSet;
import java.util.Collection;

/**
 * 布隆過濾器的實現類
 * 定義:http://en.wikipedia.org/wiki/Bloom_filter
 * 
 * @param <E>
 * 		   E指定了要插入過濾器中的元素的型別,eg,String Integer
 * @author Magnus Skjegstad <
[email protected]
> * @translator xiaoye */ public class BloomFilter<E> implements Serializable { private static final long serialVersionUID = -2326638072608273135L; private BitSet bitset; private int bitSetSize; private double bitsPerElement; private int expectedNumberOfFilterElements;//能夠新增的元素的最大個數 private int numberOfAddedElements;//過濾器容器中元素的實際數量 private int k; // 雜湊函式的個數 static final Charset charset = Charset.forName("UTF-8");//儲存雜湊值的字串的編碼方式 static final String hashName = "MD5"; //在大多數情況下,MD5提供了較好的雜湊精確度。如有必要,可以換成 SHA1演算法 static final MessageDigest digestFunction;//MessageDigest類用於為應用程式提供資訊摘要演算法的功能,如 MD5 或 SHA 演算法 static { // 初始化 MessageDigest 的摘要演算法物件 MessageDigest tmp; try { tmp = java.security.MessageDigest.getInstance(hashName); } catch (NoSuchAlgorithmException e) { tmp = null; } digestFunction = tmp; } /** * 構造一個空的布隆過濾器. 過濾器的長度為c*n * @param c * 表示每個元素佔有多少位 * @param n * 表示過濾器能新增的最大元素數量 * @param k * 表示需要使用的雜湊函式的個數 */ public BloomFilter(double c, int n, int k) { this.expectedNumberOfFilterElements = n; this.k = k; this.bitsPerElement = c; this.bitSetSize = (int) Math.ceil(c * n); numberOfAddedElements = 0; this.bitset = new BitSet(bitSetSize); } /** * 構造一個空的布隆過濾器。最優雜湊函式的個數將由過濾器的總大小和期望元素個數來確定。 * * @param bitSetSize * 指定了過濾器的總大小 * @param expectedNumberOElements * 指定了過濾器能新增的最大的元素數量 */ public BloomFilter(int bitSetSize, int expectedNumberOElements) { this(bitSetSize / (double) expectedNumberOElements, expectedNumberOElements, (int) Math.round((bitSetSize / (double) expectedNumberOElements)* Math.log(2.0))); } /** * 通過指定誤報率來構造一個過濾器。 * 每個元素所佔的位數和雜湊函式的數量會根據誤報率來得出。 * * @param falsePositiveProbability * 所期望誤報率. * @param expectedNumberOfElements * 要新增的元素的數量 */ public BloomFilter(double falsePositiveProbability, int expectedNumberOfElements) { this(Math.ceil(-(Math.log(falsePositiveProbability) / Math.log(2)))/ Math.log(2), // c = k/ln(2) expectedNumberOfElements, (int) Math.ceil(-(Math.log(falsePositiveProbability) / Math.log(2)))); // k = ln(2)m/n } /** * 根據舊過濾器的資料,重新構造一個新的過濾器 * * @param bitSetSize * 指定了過濾器所需位的大小 * @param expectedNumberOfFilterElements * 指定了過濾器所能新增的元素的最大數量 * to contain. * @param actualNumberOfFilterElements * 指定了原來過濾器的資料的數量 * <code>filterData</code> BitSet. * @param filterData * 原有過濾器中的BitSet物件 */ public BloomFilter(int bitSetSize, int expectedNumberOfFilterElements, int actualNumberOfFilterElements, BitSet filterData) { this(bitSetSize, expectedNumberOfFilterElements); this.bitset = filterData; this.numberOfAddedElements = actualNumberOfFilterElements; } /** * 根據字串的內容生成摘要 * * @param val * 字串的內容 * @param charset * 輸入資料的編碼方式 * @return 輸出為一個long型別 */ public static long createHash(String val, Charset charset) { return createHash(val.getBytes(charset)); } /** * 根據字串內容生成摘要 * * @param val * 指定了輸入的字串。預設的編碼為 UTF-8 * @return 輸出為一個long型別 */ public static long createHash(String val) { return createHash(val, charset); } /** * 根據位元組陣列生成摘要 * * @param data * 輸入資料 * @return 輸出為long型別的摘要 */ public static long createHash(byte[] data) { long h = 0; byte[] res; synchronized (digestFunction) { res = digestFunction.digest(data); } for (int i = 0; i < 4; i++) { h <<= 8; h |= ((int) res[i]) & 0xFF; } return h; } /** * 重寫equals方法 */ @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final BloomFilter<E> other = (BloomFilter<E>) obj; if (this.expectedNumberOfFilterElements != other.expectedNumberOfFilterElements) { return false; } if (this.k != other.k) { return false; } if (this.bitSetSize != other.bitSetSize) { return false; } if (this.bitset != other.bitset && (this.bitset == null || !this.bitset.equals(other.bitset))) { return false; } return true; } /** * 重寫了hashCode方法 * */ @Override public int hashCode() { int hash = 7; hash = 61 * hash + (this.bitset != null ? this.bitset.hashCode() : 0); hash = 61 * hash + this.expectedNumberOfFilterElements; hash = 61 * hash + this.bitSetSize; hash = 61 * hash + this.k; return hash; } /** * 根據最大元素數量和過濾器的大小來計算誤報率。 * 方法的返回值為誤報率。如果插入的元素個數小於最大值,則誤報率會比返回值要小。 * * @return 期望的誤報率. */ public double expectedFalsePositiveProbability() { return getFalsePositiveProbability(expectedNumberOfFilterElements); } /** * 通過插入的元素數量和過濾器容器大小來計算實際的誤報率。 * * @param numberOfElements * 插入的元素的個數. * @return 誤報率. */ public double getFalsePositiveProbability(double numberOfElements) { // (1 - e^(-k * n / m)) ^ k return Math.pow((1 - Math.exp(-k * (double) numberOfElements / (double) bitSetSize)), k); } /** * 通過實際插入的元素數量和過濾器容器大小來計算實際的誤報率。 * * @return 誤報率. */ public double getFalsePositiveProbability() { return getFalsePositiveProbability(numberOfAddedElements); } /** * 返回雜湊函式的個數 k * * @return k. */ public int getK() { return k; } /** * 清空過濾器元素 */ public void clear() { bitset.clear(); numberOfAddedElements = 0; } /** * 向過濾器中新增元素。 * 新增的元素的toString()方法將會被呼叫,返回的字串作為雜湊函式的輸出。 * * @param element * 要新增的元素 */ public void add(E element) { long hash; String valString = element.toString(); for (int x = 0; x < k; x++) { hash = createHash(valString + Integer.toString(x)); hash = hash % (long) bitSetSize; bitset.set(Math.abs((int) hash), true); } numberOfAddedElements++; } /** * 新增一個元素集合到過濾器中 * * @param c * 元素集合. */ public void addAll(Collection<? extends E> c) { for (E element : c) add(element); } /** * 用來判斷元素是否在過濾器中。如果已存在,返回 true。 * * @param element * 要檢查的元素. * @return 如果估計該元素已存在,則返回true */ public boolean contains(E element) { long hash; String valString = element.toString(); for (int x = 0; x < k; x++) { hash = createHash(valString + Integer.toString(x)); hash = hash % (long) bitSetSize; if (!bitset.get(Math.abs((int) hash))) return false; } return true; } /** * 判斷一個集合中的元素是否都在過濾器中。 * * @param c * 要檢查的元素集合 * @return 如果集合所有的元素都在過濾器中,則返回true。 */ public boolean containsAll(Collection<? extends E> c) { for (E element : c) if (!contains(element)) return false; return true; } /** * 得到某一位的值 * * @param bit * bit的位置. * @return 如果該位被設定,則返回true。 */ public boolean getBit(int bit) { return bitset.get(bit); } /** * 設定過濾器某一位的值 * * @param bit * 要設定的位置. * @param value * true表示已經成功設定。false表示改為被清除。 */ public void setBit(int bit, boolean value) { bitset.set(bit, value); } /** * 返回存放資訊的位陣列. * * @return 位陣列. */ public BitSet getBitSet() { return bitset; } /** * 得到過濾器中位陣列個大小。 * * @return 陣列大小. */ public int size() { return this.bitSetSize; } /** * 返回已新增的元素的個數 * * @return 元素個數. */ public int count() { return this.numberOfAddedElements; } /** * 得到能新增的元素的最大數量 * * @return 最大數量. */ public int getExpectedNumberOfElements() { return expectedNumberOfFilterElements; } /** * 得到每個元素佔用的位的個數的期望值 * * @return 每個元素佔用的位數 */ public double getExpectedBitsPerElement() { return this.bitsPerElement; } /** * 得到每個元素佔用位數的實際值 * * @return 每個元素佔用的位數. */ public double getBitsPerElement() { return this.bitSetSize / (double) numberOfAddedElements; } }


相關推薦

BloomFilterJava實現BloomFilter

通過前一篇文章的學習,對於 BloomFilter 的概念和原理,以及誤報率等計算方法都一個理性的認識了。在這裡,我們將用 Java'實現一個簡單的 BloomFilter 。 package pri.xiaoye.day1029; import java.io.Ser

Java實現網路語音訊號傳送

本文轉載自部落格:https://www.aliyun.com/jiaocheng/347518.html ----------------------------------------------------------------------------------------------

119Java實現TCP協議的IP地址和埠號的轉發功能

最近碰到了這樣的需求:使用者通過TCP訪問伺服器 A,伺服器 A 再把 TCP 請求轉發給伺服器 B;同時伺服器 A 把伺服器 B 返回的資料,轉發給使用者。也就是伺服器 A 作為中轉站,在使用者和伺服器 B 之間轉發資料。示意圖如下: 為了滿足這個需求,

20171210java實現簡易貪吃蛇

一、各個類之間的關係 之前學軟體工程不努力,現在徒傷悲啊,不會用visio畫類圖…大致的類、方法之間的關係就是這樣。 二、遊戲介面 三、具體實現: 1.遊戲視窗 要考慮的問題有

神經網路入門JAVA實現感知器演算法

簡述 隨著網際網路的高速發展,A(AI)B(BigData)C(Cloud)已經成為當下的核心發展方向,假如三者深度結合的話,AI是其中最核心的部分。所以如果說在未來社會,每個人都必須要學會程式設計的話,那麼對於程式設計師來說,人工智慧則是他們所必須掌握的技術(科技發展真tm快)。 這篇文章介紹

Java建立你的第一個區塊鏈-part1

區塊鏈是分散式資料儲存、點對點傳輸、共識機制、加密演算法等計算機技術的新型應用模式。所謂共識機制是區塊鏈系統中實現不同節點之間建立信任、獲取權益的數學演算法 。 前言 本系列教程旨在幫助你瞭解如何開發區塊鏈技術。 本章目標 建立你第一個非

前端jQuery實現瀑布流效果

scrollto title n) 個性 避免 ive gets type turn jQuery實現瀑布流效果 何為瀑布流:   瀑布流,又稱瀑布流式布局。是比較流行的一種網站頁面布局,視覺表現為參差不齊的多欄布局,隨著頁面滾動條向下滾動,這種布局還會不斷加載數據塊並附加

滑稽 blog 實現版本控制

隨筆 一派胡言 黑科技 版本控制 函數式 (實現方法和scheme中的鏈表思想幾乎完全相同——不過版本控制本身就是一堆指針,參考 鏈接:git教程 - 廖雪峰的官方網站)  博客提供兩個接口:寫博客,可以在博客裏放任何內容不限量評論評論可以刪除  博客常常可以修改。但是這個功能有副作用:修

UnityShader實現圖片的區域遮罩,支援半透明,實現地圖動態上色功能

一個專案,做世界地圖時,希望未開啟的地塊是線稿,新地塊開啟時,做一個上色處理。 想到的方案就是:上了色的彩圖蓋線上稿上,然後用mask 控制彩圖的區域性顯隱。 網上找了一個,可以半透明遮罩的shader:https://www.jianshu.com/p/1d9d439c28fa 要控制不同區塊

JSchecked實現複選框全選和全不選

<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf8"> <title>無標題文件</title>

人工智慧Python實現一個簡單的人臉識別,原來我和這個明星如此相似

近幾年來,興起了一股人工智慧熱潮,讓人們見到了AI的能力和強大,比如影象識別,語音識別,機器翻譯,無人駕駛等等。總體來說,AI的門檻還是比較高,不僅要學會使用框架實現,更重要的是,需要有一定的數學基礎,如線性代數,矩陣,微積分等。 幸慶的是,國內外許多大神都已經給我們造好“輪子”,我們可以直接來使用某些模型

JavaScriptJavaScript實現一個集合

add(value) :向集合新增一個新的項。 delete(value) :從集合移除一個值。 has(value) :如果值在集合中,返回 true ,否則返回 false 。 clear() :移

JavaScriptJavaScript實現一個連結串列

append(element) :向列表尾部新增一個新的項。 insert(position, element) :向列表的特定位置插入一個新的項。 indexOf(element) :返回元素在列表中的索引。如果列表中沒有該元素則返回 -1 。 removeA

MATLAB MATLAB 實現離散時間傅立葉變換(DTFT)的兩個案例分析

先給出離散時間傅立葉變換的簡單介紹: 如果 x(n) 是絕對可加的,即 那麼它的離散時間傅立葉變換給出為: w 稱為數字頻率,單位是每樣本 rad(弧度)或 (弧度/樣本)(rad/sam

PythonPython實現解一元二次方程

用Python實現解一元二次方程,來自廖雪峰Pyhton3.0教程課後習題 # -*- coding: utf-8 -*- import math def quadratic(a, b, c): if (b * b - 4 * a * c) <

JSjs實現地圖窗體自適應不同的瀏覽器高度

//呼叫方法 autodivheight(); function autodivheight(){ //函式:獲取尺寸 //獲取瀏覽器視窗高度 var winH

Shellshell實現進度條

shell基本語法前面部落格有介紹,這裡用shell實現一個簡單的進度條 #!/bin/sh 2

UnityNGUI實現血條和傷害顯示

using UnityEngine; using System.Collections; public class create_HP : MonoBehaviour {     public GameObject HP_prefab;  //預設的血條     public Transform create

c++c++實現複數類及運算子的過載

#include<iostream> using namespace std; class Complex { public: Complex(double real = 0.0, double image = 0.0)//建構函式 :_real(real

AndroidRecycleView實現可以橫向滾動的ListView效果

終於閒下來了,總結一下RecycleView的使用。 一、概述 與常見的ListView和GridView一樣,RecycleView也用來在有限的介面上展示大量的資料。它提供了一種插拔式的體驗,高度的解耦,使用非常靈活,可以通過support-v7包進行匯入。先看以下Re