JavaBitSet學習


一、背景

之前公司項目需要對會員人群進行去重過濾,人群的維度是user_id;

因此采用了BitSet做簡單的去重,方案將user_id作為bitset中的bit索引;

通過and\or\xor基礎運算實現,以公司2億會員生產bitSet來算,容量24m(不壓縮),主要的and\or\xor運算平均耗時5毫秒,按現有BitSet的數據結構,未來可以支持20億會員;

舉例:

皇冠人群:1\3\5\63\65\67\69\127

活躍人群:5\65\68\127

業務需求:

1、需要提取出既是皇冠又是活躍的會員

2、需要提取出皇冠和活躍兩部分會員,但是要保證不重復

3、需要皇冠人群中不活躍的會員

假設兩個人群的量都是千萬級的人群,我們該如何處理?

這里我們借助了JavaBitSet的位運算來實現可以這樣來實現:

需求1:

1、皇冠人群 and 活躍人群 取出交集

需求2:

1、皇冠人群 or 活躍人群 取出並集

需求3:

1、活躍人群 and 皇冠人群 取出交集

2、皇冠人群 xor 交集人群 取出非活躍的皇冠會員

二、BitSet入門:

BitSet的原理

Java BitSet可以按位存儲,計算機中一個字節(byte)占8位(bit);

而BitSet是位操作的對象,值只有0或1(即true 和 false),內部維護一個long數組,初始化只有一個long segement,所以BitSet最小的size是64;隨着存儲的元素越來越多,BitSet內部會自動擴充,一次擴充64位,最終內部是由N個long segement 來存儲;

默認情況下,BitSet所有位都是0即false;

正如上述方案來說:

皇冠人群是一個BitSet,其中1\3\5\63\65\67\69\127對應位為1;即橙色部分

活躍人群也是一個BitSet,其中5\65\68\127對應位為1;即橙色部分

而64個位為一個long數組,因此64對應的位就被分配到第2個long數組;

BitSet的應用場景

海量數據去重、排序、壓縮存儲

BitSet的基本操作

and(與)、or(或)、xor(異或)

BitSet的優缺點

優點:

l  按位存儲,內存占用空間小

l  豐富的api操作

缺點:

l  線程不安全

l  BitSet內部動態擴展long型數組,若數據稀疏會占用較大的內存

 

BitSet為什么選擇long型數組作為內部存儲結構

JDK選擇long數組作為BitSet的內部存儲結構是出於性能的考慮,and和or的時候減少循環次數,提高性能;

因為BitSet提供and和or這種操作,需要對兩個BitSet中的所有bit位做and或者or,實現的時候需要遍歷所有的數組元素。使用long能夠使得循環的次數降到最低,所以Java選擇使用long數組作為BitSet的內部存儲結構。

舉個例子:

當我們進行BitSet中的and, or, xor操作時,要對整個bitset中的bit都進行操作,需要依次讀出bitset中所有的word,如果是long數組存儲,我們可以每次讀入64個bit,而int數組存儲時,只能每次讀入32個bit

BitSet源碼解析

參考JunitTest斷點查看代碼,了解BitSet每個方法的實現邏輯

附:

源碼解析博文:http://www.cnblogs.com/lqminn/archive/2012/08/30/2664122.html

Java移位基礎知識:https://www.cnblogs.com/hongten/p/hongten_java_yiweiyunsuangfu.html

 

三、Java BitSet API簡介

BitSet()
          創建一個新的位 set。

BitSet(int nbits)
          創建一個位 set,它的初始大小足以顯式表示索引范圍在 0 到 nbits-1 的位。

 

void

and(BitSet set)
          對此目標位 set 和參數位 set 執行邏輯操作。

void

andNot(BitSet set)
          清除此 BitSet 中所有的位,其相應的位在指定的 BitSet 中已設置。

int

cardinality()
          返回此 BitSet 中設置為 true 的位數。

void

clear()
          將此 BitSet 中的所有位設置為 false

void

clear(int bitIndex)
          將索引指定處的位設置為 false

void

clear(int fromIndex, int toIndex)
          將指定的 fromIndex(包括)到指定的 toIndex(不包括)范圍內的位設置為 false

Object

clone()
          復制此 BitSet,生成一個與之相等的新 BitSet

boolean

equals(Object obj)
          將此對象與指定的對象進行比較。

void

flip(int bitIndex)
          將指定索引處的位設置為其當前值的補碼。

void

flip(int fromIndex, int toIndex)
          將指定的 fromIndex(包括)到指定的 toIndex(不包括)范圍內的每個位設置為其當前值的補碼。

boolean

get(int bitIndex)
          返回指定索引處的位值。

BitSet

get(int fromIndex, int toIndex)
          返回一個新的 BitSet,它由此 BitSet 中從 fromIndex(包括)到 toIndex(不包括)范圍內的位組成。

int

hashCode()
          返回此位 set 的哈希碼值。

boolean

intersects(BitSet set)
          如果指定的 BitSet 中有設置為 true 的位,並且在此 BitSet 中也將其設置為true,則返回 ture。

boolean

isEmpty()
          如果此 BitSet 中沒有包含任何設置為 true 的位,則返回 ture。

int

length()
          返回此 BitSet 的“邏輯大小”:BitSet 中最高設置位的索引加 1。

int

nextClearBit(int fromIndex)
          返回第一個設置為 false 的位的索引,這發生在指定的起始索引或之后的索引上。

int

nextSetBit(int fromIndex)
          返回第一個設置為 true 的位的索引,這發生在指定的起始索引或之后的索引上。

void

or(BitSet set)
          對此位 set 和位 set 參數執行邏輯操作。

void

set(int bitIndex)
          將指定索引處的位設置為 true

void

set(int bitIndex, boolean value)
          將指定索引處的位設置為指定的值。

void

set(int fromIndex, int toIndex)
          將指定的 fromIndex(包括)到指定的 toIndex(不包括)范圍內的位設置為 true

void

set(int fromIndex, int toIndex, boolean value)
          將指定的 fromIndex(包括)到指定的 toIndex(不包括)范圍內的位設置為指定的值。

int

size()
          返回此 BitSet 表示位值時實際使用空間的位數。

String

toString()
          返回此位 set 的字符串表示形式。

void

xor(BitSet set)
          對此位 set 和位 set 參數執行邏輯異或操作。

 附本人的調試代碼:

  1 package com.vip.amd.bitset;
  2 
  3 import org.junit.*;
  4 import org.junit.Test;
  5 
  6 import java.util.BitSet;
  7 
  8 /**
  9  * @author xupeng.zhang
 10  * @date 2017/12/2 0002
 11  */
 12 public class BitSetTest {
 13     //全量bitset
 14     private static BitSet allBitSet = new BitSet();
 15     //偶數bitset
 16     private static BitSet evenBitSet = new BitSet();
 17     //奇數bitset
 18     private static BitSet oddBitSet = new BitSet();
 19     //空bitset
 20     private static BitSet emptyBitSet = new BitSet();
 21 
 22     @BeforeClass
 23     public static void init(){
 24         for (int i = 0; i < 63; i++) {
 25             allBitSet.set(i);
 26             if (i % 2 == 0) {
 27                 evenBitSet.set(i);
 28             }else{
 29                 oddBitSet.set(i);
 30             }
 31         }
 32     }
 33 
 34     //測試初始化
 35     @Test
 36     public void testInit(){
 37         //斷點進去看
 38         BitSet initBitSet1 = new BitSet(55);
 39         BitSet initBitSet2 = new BitSet(129);
 40     }
 41 
 42     //測試基礎的and\or\xor運算
 43     @org.junit.Test
 44     public void testOper(){
 45         //System.out.println(evenBitSet.toByteArray());
 46         evenBitSet.and(allBitSet);
 47         System.out.println("偶數Bit and 全量Bit:"+evenBitSet);
 48         evenBitSet.xor(allBitSet);
 49         System.out.println("偶數Bit xor 全量Bit:"+evenBitSet);
 50         evenBitSet.or(allBitSet);
 51         System.out.println("偶數Bit or 全量Bit:"+evenBitSet);
 52     }
 53 
 54     //測試動態擴展,每次是以64位為單位
 55     @org.junit.Test
 56     public void testExpand(){
 57         testSize();
 58         allBitSet.set(100000000);
 59         System.out.println("全量Bit-設置64之后大小:" + allBitSet.size()/8/1024/1024+"m");
 60         System.out.println("全量Bit-設置64之后長度:" + allBitSet.length());
 61         System.out.println("全量Bit-設置64之后實際true的個數:" + allBitSet.cardinality());
 62     }
 63 
 64     //oddBitSet過濾掉evenBitSet
 65     @Test
 66     public void testOddFilterEvenBitSet(){
 67         oddBitSet.set(2);
 68         oddBitSet.set(4);
 69         oddBitSet.set(6);
 70         System.out.println("過濾前:oddBitSet:"+oddBitSet);
 71         evenBitSet.and(oddBitSet);
 72         oddBitSet.xor(evenBitSet);
 73         System.out.println("oddBitSet過濾evenBitSet相同的元素的結果:"+oddBitSet);
 74     }
 75 
 76     //偶數和奇數bitset合並去重之后和allbitSet內容一致
 77     @Test
 78     public void testOddAndEventBitSet(){
 79         oddBitSet.set(2);
 80         oddBitSet.set(4);
 81         oddBitSet.set(6);
 82         System.out.println("偶數BitSet合並前 :"+evenBitSet);
 83         System.out.println("奇數BitSet合並前 :"+oddBitSet);
 84         System.out.println("------------------------");
 85         oddBitSet.or(evenBitSet);
 86         System.out.println("偶數BitSet合並后 :"+evenBitSet);
 87         System.out.println("奇數BitSet合並后 :"+oddBitSet);
 88         System.out.println("全亮BitSet內容是 :"+allBitSet);
 89         Assert.assertTrue(oddBitSet.equals(allBitSet));
 90     }
 91 
 92 
 93     //返回true的個數
 94     @org.junit.Test
 95     public void testCardinality(){
 96         System.out.println("偶數Bit-true的個數:" + evenBitSet.cardinality());
 97     }
 98 
 99     //判斷是否為空
100     @org.junit.Test
101     public void testIsEmpty(){
102         System.out.println("全量Bit-判斷非空:" + allBitSet.isEmpty());
103         System.out.println("空  Bit-判斷非空:" + emptyBitSet.isEmpty());
104     }
105 
106     //根據下表開始結束獲取
107     @org.junit.Test
108     public void testGetFromEnd(){
109         System.out.println("全量Bit-[0,5]:" + allBitSet.get(0, 5));
110         System.out.println("空  Bit-[0,5]:" + emptyBitSet.get(0, 5));
111     }
112 
113     //判斷是否存在bitset
114     @org.junit.Test
115     public void testGet(){
116         System.out.println("全量Bit-下標為2是否存在:" + allBitSet.get(2));
117         System.out.println("偶數Bit-下標為1是否存在:" + evenBitSet.get(1));
118         System.out.println("偶數Bit-下標為2是否存在:" + evenBitSet.get(2));
119     }
120 
121     //計算bitset內存大小
122     @org.junit.Test
123     public void testSize(){
124         System.out.println("空  Bit-大小::" + emptyBitSet.size()+"byte");
125         System.out.println("偶數Bit-大小:" + evenBitSet.size() + "byte");
126         System.out.println("全量Bit-大小:" + allBitSet.size() + "byte");
127     }
128 
129     //計算bitset長度(bitset最大數+1)
130     @org.junit.Test
131     public void testLength(){
132         System.out.println("全量Bit-長度:" + allBitSet.length());
133         System.out.println("偶數Bit-長度:" + evenBitSet.length());
134     }
135 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM