解读Lucene.Net —— 一、 Directory 之一
在使用Lucene.Net中,第一个接触的类一般是Directory。它是Lucene存储的一个抽象,由此派生了两个类:FSDirectory和RAMDirectory,用于控制索引文件的存储位置。使用FSDirectory类,就是存储到硬盘;使用RAMDirectory类,则是存储到内存。
A Directory is a flat list of files. Files may be written once, when they are created. Once a file is created it may only be opened for read, or
deleted. Random access is permitted both when reading and writing. Java's i/o APIs not used directly, but rather all i/o is through this API. This permits things such as:
implementation of RAM-based indices;
implementation indices stored in a database, via JDBC;
implementation of an index as a single file;
意思就是:一个Directory对象是一份文件的清单。文件可能只在被创建的时候写一次。一旦文件被创建,它将只被读取或者删除。在读取的时候进行写入操作是允许的。Java的I/O库没有被直接使用,所以的I/O操作都通过这个API。这些存储可以允许:
实现基于内存的索引;
实现索引存入数据库,通过JDBC;
实现一个索引是一个文件。
而Directory的11个方法分别是:
1、list 把一个Directory对象下的文件,按字符串数组的方式返回;
2、fileExists 给定一个文件名,如果存在,就返回true;
3、fileModified 返回给定文件名被修改的时间;
4、touchFile 设置给定文件名文件的更新时间为现在;
5、deleteFile 删除当前directory对象下一个给定文件名的文件,该文件必须存在;
6、renameFile 重命名当前directory一个文件的文件名,如果新的名字在directory里已经存在,将会更换。这个要更换原子;
7、fileLength 返回文件的长度;
8、createFile 创建一个空文件,并且返回该文件的写入流;
9、openFile 返回一个存在文件的读取流;
10、makeLock 锁定该directory对象;
11、close 关闭该对象。
而在Directory类中,使用的都是抽象方法,把这个类换成接口也可以。
然后再来看看RAMDirectory类。RAMDirectory是Directory的内存操作实现。RAMDirectory类有5个重载构造函数。
RAMDirectory() 构造函数无操作;
RAMDirectory(Directory dir) 允许把硬盘上的索引载入内存,这个操作只适用于可以被载入内存的索引。(注:文件结构不对或者索引大小超出内存肯定就不行了。)这个构造函数只调用了RAMDirectory(Directory dir, boolean closeDir)构造函数,并未做其他动作。
再来看看RAMDirectory(Directory dir, boolean closeDir)构造函数。除了默认构造函数,其他3个构造函数都是调用的这个构造函数做处理的。
代码 1-2:
for (int i = 0; i < files.Length; i++)
{
// make place on ram disk
OutputStream os = CreateFile(System.IO.Path.GetFileName(files[i]));
// read current file
InputStream is_Renamed = dir.OpenFile(files[i]);
// and copy to ram disk
int len = (int) is_Renamed.Length();
byte[] buf = new byte[len];
is_Renamed.ReadBytes(buf, 0, len);
os.WriteBytes(buf, len);
// graceful cleanup
is_Renamed.Close();
os.Close();
}
if (closeDir)
dir.Close();
其他两个构造函数用到了FSDirectory类把文件构造成Directory对象。看看代码就行了:
代码 1-3:
public RAMDirectory(System.IO.FileInfo dir) : this(FSDirectory.GetDirectory(dir, false), true)
{
}
public RAMDirectory(System.String dir) : this(FSDirectory.GetDirectory(dir, false), true)
{
}
{
}
{
RAMFile file = new RAMFile();
files[name] = file;
return new RAMOutputStream(file);
}
{
System.String[] result = new System.String[files.Count];
int i = 0;
System.Collections.IEnumerator names = files.Keys.GetEnumerator();
while (names.MoveNext())
{
result[i++] = ((System.String) names.Current);
}
return result;
}
public override bool FileExists(System.String name)
{
RAMFile file = (RAMFile) files[name];
return file != null;
}
/// <summary>Returns the time the named file was last modified. </summary>
public override long FileModified(System.String name)
{
RAMFile file = (RAMFile) files[name];
return file.lastModified;
}
public override void TouchFile(System.String name)
{
// final boolean MONITOR = false;
RAMFile file = (RAMFile) files[name];
long ts2, ts1 = (System.DateTime.Now.Ticks - 621355968000000000) / 10000;
do
{
try
{
System.Threading.Thread.Sleep(new System.TimeSpan((System.Int64) 10000 * 0 + 100 * 1));
}
catch (System.Threading.ThreadInterruptedException)
{
}
ts2 = (System.DateTime.Now.Ticks - 621355968000000000) / 10000;
// if (MONITOR) {
// count++;
// }
}
while (ts1 == ts2);
file.lastModified = ts2;
// if (MONITOR)
// System.out.println("SLEEP COUNT: " + count);
}
通过文件名,可以从哈希表中还原出一个RAMFile对象,但是下面的代码比较难懂,为什么要减去个那数值呢?看看Java版的代码:
代码1-9
public void touchFile(String name) throws IOException {
// final boolean MONITOR = false;
RAMFile file = (RAMFile) files.get(name);
long ts2, ts1 = System.currentTimeMillis();
do {
try {
Thread.sleep(0, 1);
} catch (InterruptedException e) {
}
ts2 = System.currentTimeMillis();
// if (MONITOR) {
// count++;
// }
} while (ts1 == ts2);
file.lastModified = ts2;
// if (MONITOR)
// System.out.println("SLEEP COUNT: " + count);
}
看出来了,这段只是想计算时间的。为什么要这么计算时间呢?
去FSDirectory类看到,他的方法就要简单很多
代码1-10
public override void TouchFile(System.String name)
{
System.IO.FileInfo file = new System.IO.FileInfo(System.IO.Path.Combine(directory.FullName, name));
file.LastWriteTime = System.DateTime.Now;
}
为什么在RAMDirectory需要这么来计算,那是为了取得一种随机数的效果,尽量不会产生重复的。因为内存操作文件,而且载入内存的一般文件比较小,对文件时间的更新如果是用DateTime.Now的话就会产生误差。通过这种算法,用Thread.Sleep的方式,然后通过循环对比,让两个时间产生差距,否则很可能会一样。要是一样,对文件版本的控制就不是很好。而这个睡眠0.01ms,速度很快,不会影响性能。实际上采用下面的方式也是一样的:
代码1-11
do
{
try
{
System.Threading.Thread.Sleep(new System.TimeSpan((System.Int64)10000 * 0 + 100 * 1));
}
catch (System.Threading.ThreadInterruptedException)
{
}
ts2 = System.DateTime.Now.Ticks;
// if (MONITOR) {
// count++;
// }
}
while (ts1 == ts2);
而new System.TimeSpan((System.Int64)10000 * 0 + 100 * 1)取0.01毫秒,那是因为Thread.Sleep的另外一个重载函数,只能放入int值,即使放1,也会是1毫秒,扩大了100倍的时间。经过我的测试,这个循环需要执行几千次,这里暂且不评论这个用法的好坏。
其他方法都比较简单,很容易看懂,还要注意的就是一个MakeLock方法。转换工具导致了一个内嵌类,要分析这个方法的操作,还要看Java的源码。