Java实现80亿长字符串子串查找
来源:互联网 发布:软件开发服务外包 编辑:程序博客网 时间:2024/05/12 11:28
前言
- 本博客文章只有代码块,一些输出和
try...catch
等内容没有写在这里。 - 本博客文章的目的是熟悉
FileReader
和FileWriter
这两个类库的操作,Java新手,如有不妥之处请指正!
生成一个80亿长的字符串
- 80亿个ASCII码字符占用的空间大约为8G,所以只能分步写入硬盘。由于硬盘的速度较慢,所以一次写几个字节至文件是不太划算的,所以我们需要建立一个缓冲区,一次将缓冲区内所有的数据都写入硬盘,这样效率比较高。
建立缓冲区并随机设置数据
final int BUFFER_LEN = 8 * 1000 * 1000;char[] buffer = new char[BUFFER_LEN]; // 缓冲区大小为16Mfor (int i = 0; i < BUFFER_LEN; ++i) { buffer[i] = (char) (Math.random() * 10 + '0'); // 随机设为'0'到'9'中的值}
用
FileWriter
开始写入文件。注意!上一步设置数据时我设置的都是标准ASCII字符,而FileWriter
在我的平台(win10x64)上写入文件用的是utf-8
编码,一个标准ASCII字符输出到文件只会占用一个字节。所以我缓冲区的数据写入到文件中就只会占用8M的空间。fw = new FileWriter("big.txt");for (int i = 0; i < 1000; ++i) { fw.write(buffer);}
最后别忘了关闭文件
fw.flush();fw.close();
在我的固态硬盘上总共用了25秒,速度还可以。
插入多个子字符串
上一步我生成的字符都为数字,所以这次插入的子串只要为英文字母就可以避免冲突。这里插入100个随机字符串,长度为10~100随机。
生成随机子串。考虑到调试问题,这里的随机子串是固定的。
final int subNum = 100;char[][] subStrBuf = new char[subNum][];Random r = new Random(11111111);for (int i = 0; i < subNum; ++i) { int len = r.nextInt(90); subStrBuf[i] = new char[len]; for (int j = 0; j < len; ++j) { subStrBuf[i][j] = (char) (r.nextInt(26) + 'a'); }}
从原文件读取,读9次缓冲区就按顺序写入上一步创建的随机子串,同时写入目标文件。因为我没办法获取原文件的字符数量,只好这样。总共需要读取1000次缓冲区,所以所有的子串都能写入目标文件。
FileReader fr = new FileReader("big.txt");FileWriter fw = new FileWriter("big_sub.txt");// 建立缓冲区和计数器final int bufLen = 8 * 1000 * 1000 + 1;char[] cbuf = new char[bufLen];int n = 0;// 开始读取int len;while ((len = fr.read(cbuf)) != -1) { fw.write(cbuf, 0, len); // 读9个块写入一次子串 if (n % 9 == 0 && n / 9 < subNum) { fw.write(subStrBuf[n / 9]); } n += 1;
关闭文件
fr.close();fw.flush();fw.close();
这次需要读取+写入,所以耗时会比第一部慢,耗时71秒。
从文件中查找子串
思路是分块从文件中读取内容到缓冲区,然后再缓冲区内进行子串的查找。考虑到子串可能跨缓冲区,所以从第二次及以后的读入操作,都要保留上一次缓冲区的末尾内容,这个末尾的长度要高于子串的长度。
准备工作。设置一个数组来存放查找位置,找到多个子串的最大长度来确定缓冲区末尾的长度,以及打开文件。这里的
subStrBuf
是一个二维数组(char[][]),存放着多个随机子字符串,具体定义可以看上一步。long[] indexs = new long[subStrBuf.length];for (int i = 0; i < subStrBuf.length; ++i) { indexs[i] = -1;}int maxLen = 0;for (int i = 0; i < subStrBuf.length; ++i) { if (subStrBuf[i].length > maxLen) { maxLen = subStrBuf.length; }}FileReader fr = new FileReader(fileName);
定义缓冲区
final int bufLen = 8 * 1000 * 1000;char[] cbuf = new char[bufLen];
读取内容到缓冲区并获取子串的位置。
long n = 0; // n存放当前的文件的绝对位置while (true) { int len; // len存放读取到的内容的长度,为-1时代表文件结束 if (n == 0) { len = fr.read(cbuf); } else { // 保留上一个缓冲区的末尾作为这次缓冲区的头部。 for (int i = 0; i < maxLen; ++i) { cbuf[i] = cbuf[bufLen - maxLen + i]; } len = fr.read(cbuf, maxLen, bufLen - maxLen); } if (len == -1) break; // 文件结束 // 遍历子字符串 for (int i = 0; i < subStrBuf.length; ++i) { // 如果位置值不是-1就代表查找到了,不用再查 if (indexs[i] == -1) { // 自己写的Find函数,获取子串在cbuf中的位置 int tempIndex = Find(cbuf, subStrBuf[i]); if (tempIndex != -1) { // n为cbuf在整个文件中的位置 indexs[i] = n + tempIndex; } } } n += len;} // 文件结束
Find函数的定义。
private static int Find(char[] buf, char[] subStr) { // i为母串的位置 for (int i = 0; i < buf.length; ++i) { // j为子串的位置 int j; for (j = 0; j < subStr.length; ++j) { // 判断越界 if (i + j >= buf.length) return -1; // 判断是否是子串 if (buf[i + j] != subStr[j]) break; } // 循环正常退出就说明是子串,返回位置 if (j == subStr.length) return i; } return -1;}
因为这个原始查找算法的时间复杂度很高(O(n^2)),所以程序运行的时间为11分钟。
结果感言
- 算法很重要啊。如果查找算法足够优秀,那从文件中查找子串的时间会大大缩小。
- 我的硬盘是固态硬盘(读500M/s,写400M/s),机械硬盘(一般读150M,写100M)的运行时间应该会慢很多。
- 刚开始的时候完全没必要将文件大小设为8G,这样调试什么的很不方便。
fr.read(buf, offset, length);
这个函数我一直以为是fr.read(buf, start, end);
,结果一运行就报下标越界,找了许久才发现是用错了。IDE给参数提示的时候一定要认真啊。- 这整个程序用的是C语言的贴近底层的风格,完全没有体现出Java这种高级面向对象语言的优点,失败啊。
0 0
- Java实现80亿长字符串子串查找
- Java实现80亿长字符串子串查找(多线程升级)
- 字符串最大回文子串的查找java实现
- java中的查找子字符串
- Java实现:查找子串在字符串中出现的次数
- Java实现-字符串查找
- 实现一个字符串查找子串的函数
- 用数组实现查找字符串子串的位置-数据结构
- java 在一个字符串中查找最大对称子串
- java 查找两个字符串的最长公共子串
- 字符串子串的查找
- 查找字符串中的子串
- 字符串中查找子串
- 字符串查找最长子串
- 字符串子串的查找
- 查找--子字符串查找
- java查找子串
- KMP子字符串查找算法.java
- VC 调用JAVA
- apache2.4配置虚拟主机遇到的那些坑
- UWP应用中的双页面视图
- VerticalViewPager与VertialTabLayout的结合使用
- iOS开发中,出现错误:Apple Mach-O Linker Error
- Java实现80亿长字符串子串查找
- 【 .Net码农】【JQuery】[js学习笔记]PDF.js专题
- Java基础知识点学习笔记
- 一个程序员该读的书
- JVM terminated. Exit code=-1
- salt-minion关于以下公钥无法验证错误
- Windows下创建NFS服务器
- Android 主题和风格【1】
- 重温SQL——行转列,列转行(转)