校内搜索的底层索引我是用它做的,为什么速度这么快腻,以前只是用它的查询接口,没有深入到核心去看它的索引文件结构,今天分析了以下它的源代码主要是store包和util包这两个与文件最为密切的包。
包org/apache/lucene/util包含一些数据结构如BitVector 和PriorityQueue,还有StringHelper类主要的用途是判断两个字符串是否相等。
抽象类PriorityQueue是一个优先队列的抽象类,用于后面实现各种具体的优先队列,提供常数时间内的最小元素访问能力,内部实现机制是哈希表和堆排序算法。
Constants主要定义了一些全局的常量。用System.getProperty("java.version"),System.getProperty("os.name")获得Java版本,系统的版本号。
包org/apache/lucene/store里主要是一些对文件的操作类。其主要目的是抽象出和平台文件系统无关的存储抽象,提供诸如目录服务(增、删文件)、输入流和输出流。
主要的类的结构为:抽象类Directory类,InputStream类和OutputStream类。
其中FSDirectory,RAMDirectory继承了Directory抽象类,
FSInputStream,RAMInputStream继承了InputStream抽象类,
FSOutputStream,RAMOutputStream继承了OutputStream抽象类。
其中FS开头的是以实际的文件系统为基础的,以RAM开头的是内存中的虚拟文件系统,虚拟文件的类为:RAMFile,包含的内容为:
Vector buffers = new Vector();
long length;
long lastModified = System.currentTimeMillis();
RAMFile中采用数组来表示文件的存储空间。在此的基础上,完成各项操作的实现,就形成了基于内存的虚拟文件系统。
下面具体分析这个内存的虚拟文件系统。
RAMDirectory类中:files信息存在一个Hashtable里
Hashtable files = new Hashtable();
创造文件的函数为
public final OutputStream createFile(String name)
{
RAMFile file = new RAMFile();
files.put(name, file);
return new RAMOutputStream(file);
}
即将name 和RAMFile存到一个Hashtable中。
打开内存文件的方式为:
public final InputStream openFile(String name)
{
RAMFile file = (RAMFile)files.get(name);
return new RAMInputStream(file);
}
其实就是用一个Hashtable来在内存中形成一个文件系统。
再来看看RAMInputStream,继承了InputStream类的方法,并重写了readInternal和
seekInternal方法。
构造函数
public RAMInputStream(RAMFile f)
{
file = f;
length = file.length;
}很容易理解。
这里大量用到了抽象类InputStream的方法,先从Lucene自定义的数据类型看,
Lucene定义一个Buffer_Size的长度为1024,并定义一bufferPosition指向其在缓冲区的位置。以下是4种数据类型基本的。
数据类型一:Int表示读取4个字节的int类型。
在InputStream中:
public final int readInt() throws IOException {
return ((readByte() & 0xFF) < < 24) | ((readByte() & 0xFF) << 16)
| ((readByte() & 0xFF) << 8) | (readByte() & 0xFF);
}
其中readByte()函数为:
public final byte readByte() throws IOException
{
if (bufferPosition >= bufferLength)
refill();
return buffer[bufferPosition++];
}
数据类型二:VInt, 可变长度的整数,最小一个字节,可以是2-4个字节,如果一个字节表示不了的话。不过通常是一个字节就够了。
以下是读取Vint的函数读取2个或3个或4个字节。
public final int readVInt() throws IOException {
byte b = readByte();
int i = b & 0x7F;
for (int shift = 7; (b & 0x80) != 0; shift += 7) {
b = readByte();
i |= (b & 0x7F) < < shift;
}
return i;
}
数据类型三:Vlong,可变长度的长整数,最小一个字节,可以是2-8个字节,如果一个字节表示不了的话。不过通常是一个字节就够了。
public final long readVLong() throws IOException {
byte b = readByte();
long i = b & 0x7F;
for (int shift = 7; (b & 0x80) != 0; shift += 7) {
b = readByte();
i |= (b & 0x7FL) << shift;
}
return i;
}
数据类型四:Long,读取8个字节的数据,
public final long readVLong() throws IOException {
byte b = readByte();
long i = b & 0x7F;
for (int shift = 7; (b & 0x80) != 0; shift += 7) {
b = readByte();
i |= (b & 0x7FL) << shift;
}
return i;
}
数据类型五:VString,java中的字符串是没有像C里面一样的’/0’的结束符的,所以在文件中记录一个字符串时,在字符串前放一个VInt表示字符串的长度。
private char[] chars;
public final String readString() throws IOException {
int length = readVInt();
if (chars == null || length > chars.length)
chars = new char[length];
readChars(chars, 0, length);
return new String(chars, 0, length);
}
ReadChars函数为
public final void readChars(char[] buffer, int start, int length)
throws IOException {
final int end = start + length;
for (int i = start; i < end; i++) {
byte b = readByte();
if ((b & 0x80) == 0)
buffer[i] = (char)(b & 0x7F);
else if ((b & 0xE0) != 0xE0) {
buffer[i] = (char)(((b & 0x1F) << 6)
| (readByte() & 0x3F));
} else
buffer[i] = (char)(((b & 0x0F) << 12)
| ((readByte() & 0x3F) << 6)
| (readByte() & 0x3F));
}
}
主要功能是读取一段长度的数据转换为String类型