Lucene工具箱之OpenBitSet(一)

3.4         Lucene工具箱之OpenBitSet

在Lucene中,DocId具有这样的特征:唯一/递增。而且在搜索的过程,不同term之间的DocId集合进行逻辑运算的需求非常之多。OpenBitSet正是集合运算的利器。

3.4.1             OpenBitSet的原理

假设有一个byte,一共有8个二进制位,如下图:


0


0


0


0


0


0


0


0


0


1


2


3


4


5


6


7

如果每个二进制位表示一个数,这个Byte可以存储[0,7]共8个数。

比如存储4,6这两个数,则byte中各个二进制位的状态如下:


0


0


0


0


1


0


1


0


0


1


2


3


4


5


6


7

用二进制位的下标表示存储的数,并在将二进制位的相应状态设为1。OpentBitSet正是利用上述原理来存储数据。

3.4.2 OpenBitSet的简单应用

假设有两个集合A = {1,3,4,10,5},B={5,3,2,8}。计算A集合与B集合的并集;计算A集合与B集合的交集。

int[]
a = {1,3,4,10,5};

int [] b = {5,3,2,8};

OpenBitSet setA = new OpenBitSet();

for(int i : a) setA.set(i);

OpenBitSet setB = new OpenBitSet();

for(int i : b) setB.set(i);

OpenBitSet unionSet = setA.clone();

unionSet. union(setB); //取A与B的并集

DocIdSetIterator iterator = unionSet.iterator();

while(iterator.nextDoc()!=DocIdSetIterator.NO_MORE_DOCS){

System.out.print(iterator.docID()+", ");

}System.out.println();

OpenBitSet intersectionSet = setA.clone();

intersectionSet. intersect(setB);//取A与B的交集

iterator = intersectionSet.iterator();

while(iterator.nextDoc()!=DocIdSetIterator.NO_MORE_DOCS){

System.out.print(iterator.docID()+", ");

}System.out.println();

输出结果如下:

并集:1, 2, 3, 4, 5, 8,
10,

交集:3,
5,

3.4.3 OpenBitSet的源码分析

OpenBitSet利用二进制位来存储数据,一个long类型最高只有64位,能存储63个数。

如果存储[0,63]之间的数,需要1个long类型串联起来。

如果存储[0,127]之间的数,需要2个long类型串联起来。

如果存储[0,191]之间的数,需要3个long类型串联起来。

……

如果存储[0,(64N+m)] (N,m为非负整数,m<64)之间的数,需要N个long类型串联起来.

所以OpenBitSet的核心就是一个long类型的数组bits。

public class OpenBitSetextendsDocIdSet implements Bits, Cloneable {

protectedlong[]bits;

这个数组需要开多大呢?依据存储数据的最大值而定。OpenBitSet有构造函数如下:

publicOpenBitSet() {

this(64);

}

这个构造函数调用了另一个需要传参的构造函数:

/** Constructs an OpenBitSet large enough to
hold <code>numBits</code>.

*/

publicOpenBitSet(long numBits) {

this.numBits = numBits;

bits = new long[bits2words(numBits)];

wlen= bits.length;

}

该构造函数中调用了bits2words()方法来通过传入的参数计算bits数组的大小。

tits2words(64) = 1;表示存储[0,63]之间的数需要1个long类型。

tits2words(256)=2;表示需要存储[0,255]之间的数需要2个long类型。

依此类推……

这样传参避免我们人工计算bits数组的大小,也封装了实现原理。

OpenBitSet的数据存储

首先要清楚的是,在OpenBitSet中:

[0,63]存储在bits[0]的64个位中

[64,127]存储在bits[1]的64个位中

……

[64N,64N+63]存储在bits[N]的64个位中

任何一个非负整数,都可以表示成:64*N+m (N,m都是非负整数,m<64)。其中N表示bits数组的下标,m表示bits[N]的64个位中需要把状态置为1的二进制位的下标。

存储数据的原代码如下:

/** sets a bit, expanding the set size if necessary */

publicvoidset(longindex) {

int wordNum = expandingWordNum(index);

int bit = (int)index & 0x3f;

long bitmask = 1L
<< bit;

bits[wordNum]
|= bitmask;

}

整个set方法有4句代码,我们一句一句分析:

第1句代码求公式64*N+m中的N。参数index除以64或者 index>>6就可以了。左移6位即除以2^6=64.

第2句代码求公式64*N+m中的m。注意0x3f= 64 =(111111)2,index%64 即为 index & 0x3f 。

第3句和第4句即把bits[N]的第m位设置为1。

    最后总结一下OpenBitSet数据存储的特点:OpenBitSet无法存储重复的数据。数据存储到OpenBitSet中后就是有序的了。OpenBitSet适合存储密集程度高,且量大的数据。OpenBitSet中存储的数据适合位运算,比如取交集、并集、补集……

由于直接从word中粘贴来受到了长度的限制,我又不想在博客编辑器中重新写一遍,所以关于OpenBitSetIterator相关的内容和Lucene4.2的其它细节可以从我的《Lucene4.x源码解读》第4章4.3节中了解。OpenBitSetIterator分析了bitList的实现原理。

《Lucene4.x源码解读》会不定时更新,可以关注我的新浪微博 @帅广应s

时间: 2024-10-05 10:19:55

Lucene工具箱之OpenBitSet(一)的相关文章

lucene自定义过滤器

先介绍下查询与过滤的区别和联系,其实查询(各种Query)和过滤(各种Filter)之间非常相似,可以这样说只要用Query能完成的事,用过滤也都可以完成,它们之间可以相互转换,最大的区别就是使用过滤返回的结果集不带评分操作,而使用Query返回的结果都是带相关性评分的,所以当我们如果有一些跟评分操作没有关系的业务,优先使用Filter操作,将会获取更好的性能,其实这也是Solr里面的q参数跟fq参数的区别.  下面,开始进入正题,在这之前,散仙还是喜欢老生常谈的先来了解一下Lucene里面有关

【Java】Lucene检索引擎详解

基于Java的全文索引/检索引擎——Lucene Lucene不是一个完整的全文索引应用,而是是一个用Java写的全文索引引擎工具包,它可以方便的嵌入到各种应用中实现针对应用的全文索引/检索功能. Lucene的作者:Lucene的贡献者Doug Cutting是一位资深全文索引/检索专家,曾经是V-Twin搜索引擎(Apple的Copland操作系统的成就之一)的主要开发者,后在Excite担任高级系统架构设计师,目前从事于一些INTERNET底层架构的研究.他贡献出的Lucene的目标是为各

R数据可视化第二弹---ggplot2工具箱

ggplot2工具箱 ggplot2的图层化架构让我们以一种结构化的方法来设计和构建图形,这里每一小节解决一个特定的作图问题. 1.基本图形类型 geom_area()用于绘制面积图 geom_bar(stat="identity")绘制条形图,我们需要指定stat="identity",因为默认的统计变换会自动对值进行计数. geom_line()绘制线条图,从左到右连接 geom_point()绘制散点图 geom_polygon()绘制多边形 geom_tex

lucene中Field.Index,Field.Store的一些设置

lucene在doc.add(new Field("content",curArt.getContent(),Field.Store.NO,Field.Index.TOKENIZED)); Field有两个属性可选:存储和索引. 通过存储属性你可以控制是否对这个Field进行存储: 通过索引属性你可以控制是否对该Field进行索引. 事实上对这两个属性的正确组合很重要. Field.Index Field.Store 说明 TOKENIZED(分词) YES 被分词索引且存储 TOKE

Lucene 基础理论

1. 全文检索系统与Lucene简介 1.1 什么是全文检索与全文检索系统 全文检索是指计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式.这个过程类似于通过字典中的检索字表查字的过程. 全文检索的方法主要分为按字检索和按词检索两种.按字检索是指对于文章中的每一个字都建立索引,检索时将词分解为字的组合.对于各种不同的语言而言,字有不同的含义,比如英文中字与词实际上

一步一步跟我学习lucene(19)---lucene增量更新和NRT(near-real-time)Query近实时查询

这两天加班,不能兼顾博客的更新,请大家见谅. 有时候我们创建完索引之后,数据源可能有更新的内容,而我们又想像数据库那样能直接体现在查询中,这里就是我们所说的增量索引.对于这样的需求我们怎么来实现呢?lucene内部是没有提供这种增量索引的实现的: 这里我们一般可能会想到,将之前的索引全部删除,然后进行索引的重建.对于这种做法,如果数据源的条数不是特别大的情况下倒还可以,如果数据源的条数特别大的话,势必会造成查询数据耗时,同时索引的构建也是比较耗时的,几相叠加,势必可能造成查询的时候数据缺失的情况

一步一步跟我学习lucene(9)---lucene搜索之拼写检查和相似度查询提示(spellcheck)

suggest应用场景 用户的输入行为是不确定的,而我们在写程序的时候总是想让用户按照指定的内容或指定格式的内容进行搜索,这里就要进行人工干预用户输入的搜索条件了:我们在用百度谷歌等搜索引擎的时候经常会看到按键放下的时候直接会提示用户是否想搜索某些相关的内容,恰好lucene在开发的时候想到了这一点,lucene提供的suggest包正是用来解决上述问题的. suggest包联想词相关介绍 suggest包提供了lucene的自动补全或者拼写检查的支持: 拼写检查相关的类在org.apache.

Lucene的一个简单的标准测试(Lucene包基于3.5版本的)

Lucene编程一般分为:索引.分词.搜索 索引源代码: package lucene的一个标准测试; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.Date; import org.apache.lucene.anal

利用Lucene把文本的字体格式进行修改,然后输出到一个新的文件中

这里书中写的是charactorProcess(File file, String destFile) 这里被我改成,(String file,  String destFIle) 一个代表现有的文件和要新建的文件 代码: package com; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.