我有一个大约100万字的大文本文件。我正在为Android手机游戏做这个,我只是想看看文本文件中是否存在单词。将任何内容加载到内存中都不是一个选择。 Android手机内存和处理器非常弱,读取此文件大约需要20秒。
我修改了这个文本文件的单词,宽度相等。对于换行,每个单词为50个字符+ 1。但是,我对如何正确实现二进制搜索感到有些困惑,因为我一直对我应该为seek()正常工作添加多少字节感到困惑。
public static long search(RandomAccessFile file, String target)
throws IOException {
file.seek(0);
String line = file.readLine();
if(line.equals(target))
return 1;
long start = 0;
long end = file.length();
long mid = (start + end -50)/2;
while(start <= end)
{
file.seek(mid);
line = file.readLine();
if(line.compareTo(target) < 0)
start = mid + 51;
else if(line.equalsIgnoreCase(target))
return 1;
else
end = mid - 51;
mid = (start + end)/2;
}
if(start > end)
return 0;
return -1;
}
我第一次设置结束时减去50,因为最后一个单词没有换行。经过几次迭代后,这将停止正常工作。我无法弄清楚如何正确地完成这项工作。任何人都可以指导我做错了吗?
答案 0 :(得分:2)
通过将文件包装在AbstractList中,您可以利用开箱即用的二进制搜索实现:
final int size = (int) ((file.length() + LINE_BREAK_LEN) / (WORD_LEN + LINE_BREAK_LEN));
return Collections.binarySearch(
new AbstractList<String>() {
public String get(int pIdx) {
try {
file.seek((WORD_LEN + LINE_BREAK_LEN) * pIdx);
return file.readLine();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
public int size() {return size;}
},
target,
Comparator.comparing(String::toLowerCase)
);
请注意,换行符只会使代码复杂化,可以从文件中省略。
答案 1 :(得分:1)
Waite 的回答很好,但缺少标记接口 RandomAccess
的实现。
没有它,Collections.binarySearch
默认执行顺序 O(n)
搜索,这是您绝对不想要的。
不幸的是,Java 似乎不允许匿名类既扩展又实现(或实现多于 1 件事),因此您需要使用稍微冗长的替代方法:
public static long search(RandomAccessFile file, String target) throws IOException {
final int size = (int) ((file.length() + LINE_BREAK_LEN) / (WORD_LEN + LINE_BREAK_LEN));
class FileAsList extends AbstractList<String> implements RandomAccess {
@Override
public String get(int pIdx) {
try {
file.seek((WORD_LEN + LINE_BREAK_LEN) * pIdx);
return file.readLine();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
@Override
public int size() {
return size;
}
}
var list = new FileAsList();
return Collections.binarySearch(list, target, Comparator.comparing(String::toLowerCase));
}