1、位图法介绍
位图的基本概念是用一个位(bit)来标记某个数据的存放状态,由于采用了位为单位来存放数据,所以节省了大量的空间。举个具体的例子,在Java中一般一个int数字要占用32位,如果能用一位就表示这个数,就可以缩减大量的存储空间。一般把这种方法称为位图法,即Bitmap。
位图法比较适合于判断是否存在这样的问题,元素的状态比较少,元素的个数比较多的情况之下。那么具体咋么做呢,这样,非常简单明了就是,2.5亿个整数里面,我维护一个长度等于最大整数值得字符串,每个整数是否存在我就在该整数对应的位置置为1,比如,有{2, 4, 5, 6, 67, 5}这么几个整数,我维护一个 00…0000 67位的字符串。但是,如果你不知道整数的最大值,你至少需要一个长度2^ 32的字符串,因为整数的最大值就是2^ 32,(int占4个字节,因此是32位),那这就最少是512M内存,从char的长度算内存会算吧,直接、最大整数/8*2^20 就是M的单位。那这么说来就可以理解位图法了。
位图映射规则
位图映射的规则就是 为一长串的二进制数的对应位标记为1就行了,其优点就是不用像存于hashmap的key那样耗内存,比如我们存储的是数组中的第一二个字节:
1000000000001000
表示在位图中存储的是16 与 4 这两个数
2、BitSet
正因为位图运算在空间方面的优越性,很多语言都有直接对它的支持。如在C++的STL库中就有一个bitset容器。而在Java中,在java.util包下也有一个BitSet类用来实现位图运算。此类实现了一个按需增长的位向量。BitSet的每一位都由一个boolean值来表示。用非负的整数将BitSet的位编入索引,可以对每个编入索引的位进行测试、设置或者清除。通过逻辑与、逻辑或和逻辑异或操作,可以使用一个BitSet修改另一个BitSet的内容。
BitSet底层实现是通过一个long数组来保存数据的,也就是说它增长的最小单位是一个long所占的逻辑位,即64位。
下面用代码来演示用位图法来实现查找数组中的重复数据:
package com.xwt1208bitmap;
//查找重复并排序
import java.util.Arrays;
import java.util.Random;
/**
*
* @author xwt
*
*/
public class Test{
int ARRNUM = 10;
int LEN_INT = 32;
int mmax = 1008;
int mmin = 1000;
int N = mmax - mmin + 1;
public static void main(String args[]) {
new Test().findDuplicate();
}
public void findDuplicate() {
int[] array = getArray(ARRNUM);
int[] bitArray = setBit(array);
printBitArray(bitArray);
}
public void printBitArray(int[] bitArray) {
int count = 0;
System.out.print("有重复的数据为:");
for (int i = 0; i < N; i++) {
if (getBit(bitArray, i) != 0) {
count++;
System.out.print(i + mmin + ", ");
}
}
System.out.println();
System.out.println("找出有重复的数据并排序后的数组大小为:" + count);
}
public int getBit(int[] bitArray, int k) {// 1右移 k % 32位 与上 数组下标为 k/32 位置的值
return bitArray[k / LEN_INT] & (1 << (k % LEN_INT));
}
public int[] setBit(int[] array) {// 首先取得数组位置下标 i/32, 然后 或上 在该位置int类型数值的bit位:i % 32
int m = array.length;
int bit_arr_len = N / LEN_INT + 1;
int[] bitArray = new int[bit_arr_len];
int[] bitArray1 = new int[bit_arr_len];
for (int i = 0; i < m; i++) {
int num = array[i] - mmin;
if((bitArray[num/LEN_INT] & (1 << (num % LEN_INT)))!=0) {
bitArray1[num/LEN_INT] |= (1 << (num % LEN_INT));
}
bitArray[num/LEN_INT] |= (1 << (num % LEN_INT));
}
return bitArray1;
}
public int[] getArray(int ARRNUM) {
int array[] = new int[ARRNUM];
System.out.println("原始数组大小:" + ARRNUM);
Random r = new Random();
for (int i = 0; i < ARRNUM; i++) {
array[i] = r.nextInt(N) + mmin;
}
System.out.println(Arrays.toString(array));
return array;
}
}
某一次运行后的输出为:
原始数组大小:10
[1003, 1005, 1003, 1006, 1001, 1001, 1007, 1003, 1000, 1006]
有重复的数据为:1001, 1003, 1006,
找出有重复的数据并排序后的数组大小为:3