算法实现

set 集合实现查找

将1 亿个数存储在 set 集合内, 然后根据 contains 方法来判断该整数是否存在集合中即可。

 

bitmap 位图实现查找

程序实现

public class AnswerApp {

public static void main(String[] args) {
long startTime = System.currentTimeMillis();

BitSet bitSet = new BitSet(Integer.MAX_VALUE);

int count = 1 << 27;
System.out.println("本次要在 " + count + " 个数中进行查看某个整数是否在其中");
System.out.println();

System.out.println("加载数据到内存中...\n");
// 随机生成 Integer.MAX_VALUE 以内的 count 个整数
for (int i = 0; i < count; i++) {
bitSet.set(new Random().nextInt(Integer.MAX_VALUE));
}
System.out.println("加载数据到内存耗时: " + (System.currentTimeMillis() - startTime) + " ms.");
startTime = System.currentTimeMillis();
System.out.println();

System.out.println("查找中...");
System.out.println("bitSet 的总大小: " + bitSet.length());
System.out.println();

System.out.println("整数 1024 是否在" + count +"个数当中: " + bitSet.get(1024));
System.out.println("整数 20190607 是否在" + count +"个数当中: " + bitSet.get(20190607));
System.out.println();

// 把整数 20190607 放到数字容器中
bitSet.set(20190607);
// 此时再看 20190607 是否存在数字容器内
System.out.println("整数 20190607 是否在" + count +"个数当中: " + bitSet.get(20190607));
System.out.println();

// 把整数 20190607 从数字容器内移除
bitSet.set(20190607, false);
// 此时再看 20190607 是否存在数字容器内
System.out.println("整数 20190607 是否在" + count +"个数当中: " + bitSet.get(20190607));
System.out.println();

System.out.println("查找数据耗时: " + (System.currentTimeMillis() - startTime) + " ms.");
}
}

 

运行输出

本次要在 134217728 个数中进行查看某个整数是否在其中

加载数据到内存中...

加载数据到内存耗时: 21029 ms.

查找中...
bitSet 的总大小: 2147483641

整数 1024 是否在134217728个数当中: false
整数 20190607 是否在134217728个数当中: false

整数 20190607 是否在134217728个数当中: true

整数 20190607 是否在134217728个数当中: false

查找数据耗时: 0 ms.

 

两种方式占用内存对比

  • 采用 set 集合实现, 1亿个int类型整数占用空间是 4亿字节 大约是 400MB
  • 1亿 int 整数 = 4字节 * 1 亿 = 4亿字节
  • 采用位图实现, 因为 一个int 类型是32位, 所以占用的空间是 set 方式的 1/32, 即 400/32=12.5MB

 

换算规则

  • 1GB = 1024MB = ​​1024 * 1024 KB​​ = ​​1024 * 1024 * 1024 Byte​​ = 1073741824 字节 ​​约等于​​ 十亿字节
  • int 值的取值范围 -2147483648(-2的31次方) ~ 2147483647(2的31次方 - 1)
  • 1个 int 类型整数 = 二进制位数32位(bit) = 4字节(byte)

 

BitSet类

BitSet中实现了Cloneable接口中定义的方法如下表所列:

序号

方法描述

1

void and(BitSet set) 对此目标位 set 和参数位 set 执行逻辑与操作。

2

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

3

int cardinality( ) 返回此 BitSet 中设置为 true 的位数。

4

void clear( ) 将此 BitSet 中的所有位设置为 false。

5

void clear(int index) 将索引指定处的位设置为 false。

6

void clear(int startIndex, int endIndex) 将指定的 startIndex(包括)到指定的 toIndex(不包括)范围内的位设置为 false。

7

Object clone( ) 复制此 BitSet,生成一个与之相等的新 BitSet。

8

boolean equals(Object bitSet) 将此对象与指定的对象进行比较。

9

void flip(int index) 将指定索引处的位设置为其当前值的补码。

10

void flip(int startIndex, int endIndex) 将指定的 fromIndex(包括)到指定的 toIndex(不包括)范围内的每个位设置为其当前值的补码。

11

boolean get(int index) 返回指定索引处的位值。

12

BitSet get(int startIndex, int endIndex) 返回一个新的 BitSet,它由此 BitSet 中从 fromIndex(包括)到 toIndex(不包括)范围内的位组成。

13

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

14

boolean intersects(BitSet bitSet) 如果指定的 BitSet 中有设置为 true 的位,并且在此 BitSet 中也将其设置为 true,则返回 true。

15

boolean isEmpty( ) 如果此 BitSet 中没有包含任何设置为 true 的位,则返回 true。

16

int length( ) 返回此 BitSet 的"逻辑大小":BitSet 中最高设置位的索引加 1。

17

int nextClearBit(int startIndex) 返回第一个设置为 false 的位的索引,这发生在指定的起始索引或之后的索引上。

18

int nextSetBit(int startIndex) 返回第一个设置为 true 的位的索引,这发生在指定的起始索引或之后的索引上。

19

void or(BitSet bitSet) 对此位 set 和位 set 参数执行逻辑或操作。

20

void set(int index) 将指定索引处的位设置为 true。

21

void set(int index, boolean v) 将指定索引处的位设置为指定的值。

22

void set(int startIndex, int endIndex) 将指定的 fromIndex(包括)到指定的 toIndex(不包括)范围内的位设置为 true。

23

void set(int startIndex, int endIndex, boolean v) 将指定的 fromIndex(包括)到指定的 toIndex(不包括)范围内的位设置为指定的值。

24

int size( ) 返回此 BitSet 表示位值时实际使用空间的位数。

25

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

26

void xor(BitSet bitSet) 对此位 set 和位 set 参数执行逻辑异或操作。

​Java Bitset类​

 

public static void main(String[] args) {
// nbits为2的倍数, 如果不是2的倍数也会转为2的倍数, 如new BitSet(200), bitSet.size()=256
BitSet bitSet = new BitSet(128);
System.out.println(bitSet.length());
System.out.println(bitSet.size());
System.out.println(bitSet.cardinality());

System.out.println("---------------");

bitSet.set(0);
bitSet.set(128); // 代码1
bitSet.set(12);
bitSet.set(8);
bitSet.set(38);

System.out.println(bitSet.length());
System.out.println(bitSet.size());
System.out.println(bitSet.cardinality());
System.out.println(bitSet.toString());
}

代码1处没注释时输出

0
128
0
---------------
129 // 为true的最大的为128
256 // 因为128已经超过初始化容量,所以2倍扩容(从0开始算, 因为放了128, 所以超过了容量进行扩容)
5 // 共有5个位数设置为true
{0, 8, 12, 38, 128}

 

注释掉代码1处时输出

0
128
0
---------------
39 // 为true的最大的为38
128 // 初始化容量为128
4
{0, 8, 12, 38}

 

Reference