引言:在使用电脑的时候,难免会遇到找不到本地文件的情况,而我们通常又记得文件名或者其中的几个关键字。当然一般系统都提供搜索方法,如windows的搜索,但其不全是基于文件名的,因而搜索效率显得低下。在基于文件名的文件搜索模式下,市面上有优秀的成品软件---著名的Everything,其原理我不多说了,简单一查就明白。下面我就谈谈我们用Java实现的一种基于文件名的文件搜索,希望大家提出更好的修改意见~~
1. 总的思路:先将文件系统的所有文件名(带路径)读入内存;再组织成一定数据结构的索引(方便检索用);同时添加监听器,响应文件系统的变化;退出时,将内存中对应的索引信息压缩存储到硬盘上;再次启动时,先从硬盘读入文件名信息、处理,然后在后台扫描磁盘,更新索引。
2. 文件名信息的获取:扫描全盘,可以采用递归或非递归的办法。如下非递归扫描:
Stack<String> folderStack = newStack<String>();//存放文件夹的临时栈
File[] roots = File.listRoots();//获取所有磁盘盘符
for (int i = roots.length - 1; i >= 0; i--)
folderStack.push(roots[i].toString());
while (!folderStack.isEmpty())// 遍历
{
File[] fl = new File(folderStack.pop()).listFiles();
if (fl == null)
continue;
for (int i = 0; i < fl.length; i++) {
if (fl[i].isDirectory()) {
folderStack.push(fl[i].getAbsolutePath());
} else {
index.add(fl[i].getAbsolutePath());
}
}
}
该方法实现简单,但是效率低下。一种优化办法可以是借鉴Everything的方法,用C/C++写一个dll,在java代码里调用,后台执行。
3. 数据结构:改写的HashMap,其Key-Value类型是<String,ArrayList>。为了插入、删除的方便,我们改写了它此部分的实现:
public boolean put(String key, String value)
{
if(key == null)
return false;
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for(Entry e = table[i]; e!=null; e=e.next)
{
/*
* 处理当前key已存在于hashMap的情况
*/
if(e.hash == hash&&(key.equals(e.key)))
{
/*
* 若value列表中存在当前value,插入失败
*/
if(e.value.contains(value))
return false;
e.value.add(value);
size++;
return true;
}
}
modCount++;
size++;
addEntry(hash,key,value,i);
return true;
}
删除实现在此就不列举了。考虑到一般电脑文件系统的文件数在10W以上的数量级,我们在确定HashMap的初始容量时需特别考虑,它直接影响构建Map的效率。
采用此HashMap带来的问题是:若是文件的全名,则可以根据文件名的散列值直接找到Value;否则,需要遍历整个Map;若要支持正则查询,需要处理查询条件,仍需遍历整个Map。
4. 监听器:由于文件系统的文件可以随时变化,所以在程序运行期间,我们需要检测其变化,并作出响应。这里,我们采用JNotify来实现。利用JNotify和Java多线程,我们完成了对多个磁盘的监听和处理。
5. 索引压缩存储:由于我们时间有限,直接采用了Java 包里提供的zip压缩,最终将几十兆的索引文件压缩到十兆之内。实际上,考虑到这些索引中以英文居多,我们可以采用更有效地压缩算法。
好了,就先写这么多吧。希望大家提出更好的办法~~