CountingBloomFilter.java
上传用户:quxuerui
上传日期:2018-01-08
资源大小:41811k
文件大小:10k
源码类别:

网格计算

开发平台:

Java

  1. /**
  2.  *
  3.  * Copyright (c) 2005, European Commission project OneLab under contract 034819 (http://www.one-lab.org)
  4.  * All rights reserved.
  5.  * Redistribution and use in source and binary forms, with or 
  6.  * without modification, are permitted provided that the following 
  7.  * conditions are met:
  8.  *  - Redistributions of source code must retain the above copyright 
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  *  - Redistributions in binary form must reproduce the above copyright 
  11.  *    notice, this list of conditions and the following disclaimer in 
  12.  *    the documentation and/or other materials provided with the distribution.
  13.  *  - Neither the name of the University Catholique de Louvain - UCL
  14.  *    nor the names of its contributors may be used to endorse or 
  15.  *    promote products derived from this software without specific prior 
  16.  *    written permission.
  17.  *    
  18.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
  19.  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
  20.  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
  21.  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
  22.  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
  23.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
  24.  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
  25.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
  26.  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
  27.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
  28.  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
  29.  * POSSIBILITY OF SUCH DAMAGE.
  30.  */
  31. /**
  32.  * Licensed to the Apache Software Foundation (ASF) under one
  33.  * or more contributor license agreements.  See the NOTICE file
  34.  * distributed with this work for additional information
  35.  * regarding copyright ownership.  The ASF licenses this file
  36.  * to you under the Apache License, Version 2.0 (the
  37.  * "License"); you may not use this file except in compliance
  38.  * with the License.  You may obtain a copy of the License at
  39.  *
  40.  *     http://www.apache.org/licenses/LICENSE-2.0
  41.  *
  42.  * Unless required by applicable law or agreed to in writing, software
  43.  * distributed under the License is distributed on an "AS IS" BASIS,
  44.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  45.  * See the License for the specific language governing permissions and
  46.  * limitations under the License.
  47.  */
  48. package org.apache.hadoop.util.bloom;
  49. import java.io.DataInput;
  50. import java.io.DataOutput;
  51. import java.io.IOException;
  52. /**
  53.  * Implements a <i>counting Bloom filter</i>, as defined by Fan et al. in a ToN
  54.  * 2000 paper.
  55.  * <p>
  56.  * A counting Bloom filter is an improvement to standard a Bloom filter as it
  57.  * allows dynamic additions and deletions of set membership information.  This 
  58.  * is achieved through the use of a counting vector instead of a bit vector.
  59.  * <p>
  60.  * Originally created by
  61.  * <a href="http://www.one-lab.org">European Commission One-Lab Project 034819</a>.
  62.  *
  63.  * @see Filter The general behavior of a filter
  64.  * 
  65.  * @see <a href="http://portal.acm.org/citation.cfm?id=343571.343572">Summary cache: a scalable wide-area web cache sharing protocol</a>
  66.  */
  67. public final class CountingBloomFilter extends Filter {
  68.   /** Storage for the counting buckets */
  69.   private long[] buckets;
  70.   /** We are using 4bit buckets, so each bucket can count to 15 */
  71.   private final static long BUCKET_MAX_VALUE = 15;
  72.   /** Default constructor - use with readFields */
  73.   public CountingBloomFilter() {}
  74.   
  75.   /**
  76.    * Constructor
  77.    * @param vectorSize The vector size of <i>this</i> filter.
  78.    * @param nbHash The number of hash function to consider.
  79.    * @param hashType type of the hashing function (see
  80.    * {@link org.apache.hadoop.util.hash.Hash}).
  81.    */
  82.   public CountingBloomFilter(int vectorSize, int nbHash, int hashType) {
  83.     super(vectorSize, nbHash, hashType);
  84.     buckets = new long[buckets2words(vectorSize)];
  85.   }
  86.   /** returns the number of 64 bit words it would take to hold vectorSize buckets */
  87.   private static int buckets2words(int vectorSize) {
  88.    return ((vectorSize - 1) >>> 4) + 1;
  89.   }
  90.   @Override
  91.   public void add(Key key) {
  92.     if(key == null) {
  93.       throw new NullPointerException("key can not be null");
  94.     }
  95.     int[] h = hash.hash(key);
  96.     hash.clear();
  97.     for(int i = 0; i < nbHash; i++) {
  98.       // find the bucket
  99.       int wordNum = h[i] >> 4;          // div 16
  100.       int bucketShift = (h[i] & 0x0f) << 2;  // (mod 16) * 4
  101.       
  102.       long bucketMask = 15L << bucketShift;
  103.       long bucketValue = (buckets[wordNum] & bucketMask) >>> bucketShift;
  104.       
  105.       // only increment if the count in the bucket is less than BUCKET_MAX_VALUE
  106.       if(bucketValue < BUCKET_MAX_VALUE) {
  107.         // increment by 1
  108.         buckets[wordNum] = (buckets[wordNum] & ~bucketMask) | ((bucketValue + 1) << bucketShift);
  109.       }
  110.     }
  111.   }
  112.   /**
  113.    * Removes a specified key from <i>this</i> counting Bloom filter.
  114.    * <p>
  115.    * <b>Invariant</b>: nothing happens if the specified key does not belong to <i>this</i> counter Bloom filter.
  116.    * @param key The key to remove.
  117.    */
  118.   public void delete(Key key) {
  119.     if(key == null) {
  120.       throw new NullPointerException("Key may not be null");
  121.     }
  122.     if(!membershipTest(key)) {
  123.       throw new IllegalArgumentException("Key is not a member");
  124.     }
  125.     int[] h = hash.hash(key);
  126.     hash.clear();
  127.     for(int i = 0; i < nbHash; i++) {
  128.       // find the bucket
  129.       int wordNum = h[i] >> 4;          // div 16
  130.       int bucketShift = (h[i] & 0x0f) << 2;  // (mod 16) * 4
  131.       
  132.       long bucketMask = 15L << bucketShift;
  133.       long bucketValue = (buckets[wordNum] & bucketMask) >>> bucketShift;
  134.       
  135.       // only decrement if the count in the bucket is between 0 and BUCKET_MAX_VALUE
  136.       if(bucketValue >= 1 && bucketValue < BUCKET_MAX_VALUE) {
  137.         // decrement by 1
  138.         buckets[wordNum] = (buckets[wordNum] & ~bucketMask) | ((bucketValue - 1) << bucketShift);
  139.       }
  140.     }
  141.   }
  142.   @Override
  143.   public void and(Filter filter) {
  144.     if(filter == null
  145.         || !(filter instanceof CountingBloomFilter)
  146.         || filter.vectorSize != this.vectorSize
  147.         || filter.nbHash != this.nbHash) {
  148.       throw new IllegalArgumentException("filters cannot be and-ed");
  149.     }
  150.     CountingBloomFilter cbf = (CountingBloomFilter)filter;
  151.     
  152.     int sizeInWords = buckets2words(vectorSize);
  153.     for(int i = 0; i < sizeInWords; i++) {
  154.       this.buckets[i] &= cbf.buckets[i];
  155.     }
  156.   }
  157.   @Override
  158.   public boolean membershipTest(Key key) {
  159.     if(key == null) {
  160.       throw new NullPointerException("Key may not be null");
  161.     }
  162.     int[] h = hash.hash(key);
  163.     hash.clear();
  164.     for(int i = 0; i < nbHash; i++) {
  165.       // find the bucket
  166.       int wordNum = h[i] >> 4;          // div 16
  167.       int bucketShift = (h[i] & 0x0f) << 2;  // (mod 16) * 4
  168.       long bucketMask = 15L << bucketShift;
  169.       if((buckets[wordNum] & bucketMask) == 0) {
  170.         return false;
  171.       }
  172.     }
  173.     return true;
  174.   }
  175.   /**
  176.    * This method calculates an approximate count of the key, i.e. how many
  177.    * times the key was added to the filter. This allows the filter to be
  178.    * used as an approximate <code>key -&gt; count</code> map.
  179.    * <p>NOTE: due to the bucket size of this filter, inserting the same
  180.    * key more than 15 times will cause an overflow at all filter positions
  181.    * associated with this key, and it will significantly increase the error
  182.    * rate for this and other keys. For this reason the filter can only be
  183.    * used to store small count values <code>0 &lt;= N &lt;&lt; 15</code>.
  184.    * @param key key to be tested
  185.    * @return 0 if the key is not present. Otherwise, a positive value v will
  186.    * be returned such that <code>v == count</code> with probability equal to the
  187.    * error rate of this filter, and <code>v &gt; count</code> otherwise.
  188.    * Additionally, if the filter experienced an underflow as a result of
  189.    * {@link #delete(Key)} operation, the return value may be lower than the
  190.    * <code>count</code> with the probability of the false negative rate of such
  191.    * filter.
  192.    */
  193.   public int approximateCount(Key key) {
  194.     int res = Integer.MAX_VALUE;
  195.     int[] h = hash.hash(key);
  196.     hash.clear();
  197.     for (int i = 0; i < nbHash; i++) {
  198.       // find the bucket
  199.       int wordNum = h[i] >> 4;          // div 16
  200.       int bucketShift = (h[i] & 0x0f) << 2;  // (mod 16) * 4
  201.       
  202.       long bucketMask = 15L << bucketShift;
  203.       long bucketValue = (buckets[wordNum] & bucketMask) >>> bucketShift;
  204.       if (bucketValue < res) res = (int)bucketValue;
  205.     }
  206.     if (res != Integer.MAX_VALUE) {
  207.       return res;
  208.     } else {
  209.       return 0;
  210.     }
  211.   }
  212.   @Override
  213.   public void not() {
  214.     throw new UnsupportedOperationException("not() is undefined for "
  215.         + this.getClass().getName());
  216.   }
  217.   @Override
  218.   public void or(Filter filter) {
  219.     if(filter == null
  220.         || !(filter instanceof CountingBloomFilter)
  221.         || filter.vectorSize != this.vectorSize
  222.         || filter.nbHash != this.nbHash) {
  223.       throw new IllegalArgumentException("filters cannot be or-ed");
  224.     }
  225.     CountingBloomFilter cbf = (CountingBloomFilter)filter;
  226.     int sizeInWords = buckets2words(vectorSize);
  227.     for(int i = 0; i < sizeInWords; i++) {
  228.       this.buckets[i] |= cbf.buckets[i];
  229.     }
  230.   }
  231.   @Override
  232.   public void xor(Filter filter) {
  233.     throw new UnsupportedOperationException("xor() is undefined for "
  234.         + this.getClass().getName());
  235.   }
  236.   @Override
  237.   public String toString() {
  238.     StringBuilder res = new StringBuilder();
  239.     for(int i = 0; i < vectorSize; i++) {
  240.       if(i > 0) {
  241.         res.append(" ");
  242.       }
  243.       
  244.       int wordNum = i >> 4;          // div 16
  245.       int bucketShift = (i & 0x0f) << 2;  // (mod 16) * 4
  246.       
  247.       long bucketMask = 15L << bucketShift;
  248.       long bucketValue = (buckets[wordNum] & bucketMask) >>> bucketShift;
  249.       
  250.       res.append(bucketValue);
  251.     }
  252.     return res.toString();
  253.   }
  254.   // Writable
  255.   @Override
  256.   public void write(DataOutput out) throws IOException {
  257.     super.write(out);
  258.     int sizeInWords = buckets2words(vectorSize);
  259.     for(int i = 0; i < sizeInWords; i++) {
  260.       out.writeLong(buckets[i]);
  261.     }
  262.   }
  263.   @Override
  264.   public void readFields(DataInput in) throws IOException {
  265.     super.readFields(in);
  266.     int sizeInWords = buckets2words(vectorSize);
  267.     buckets = new long[sizeInWords];
  268.     for(int i = 0; i < sizeInWords; i++) {
  269.       buckets[i] = in.readLong();
  270.     }
  271.   }
  272. }