我这边生成单词是1gb 内存会很卡,你不要写那么大
1.先把1GB的大文件拆分成2048个文件,每个文件512kb,1*1024*1024/512=2048,
就是这么算出来的
2. 在把大文件的数据,放入2048个小文件中
3.把每个小文件的数据重新排序,因为不超过1mb,所以可以直接放到内存中排序
4.把排序好的小文件,在写入到大文件中,使用堆排序,堆中的是流,不会占用那么多的内存
只会比较所有的文件的一行数据
5.然后把排序好的大文件的内容,按照小顶堆前100个放入,去对比词频,
堆顶的元素就是最小的,比堆顶小的就不管,比堆顶大的,移除堆顶元素,放入新的数据
这样最后就是100个词频最高的单词了
package com.example.demo.entity;
import com.example.demo.mapper.Pair;
import org.springframework.util.CollectionUtils;
import java.io.*;
import java.nio.Buffer;
import java.util.*;
public class FileUtils {
/**
* 获取输入流 读取文件数据
* @param str
* @return
*/
public static BufferedReader getReader(String str){
BufferedReader bufferedReader= null;
try {
bufferedReader = new BufferedReader(new FileReader(str));
} catch (IOException e) {
e.printStackTrace();
}
return bufferedReader;
}
/**
* 获取输出流 写入文件
* @return
*/
public static BufferedWriter getWrite(String str){
BufferedWriter bufferedWriter= null;
try {
bufferedWriter = new BufferedWriter(new FileWriter(str));
} catch (IOException e) {
e.printStackTrace();
}
return bufferedWriter;
}
/**
* 关闭输入流
*/
public static void closeReader(BufferedReader bufferedReader){
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 关闭输出流 把缓冲区的内容写入到文件中
*/
public static void closeWriter(BufferedWriter bufferedWriter){
try {
bufferedWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 生成1个G单词
*/
public static void generate(String str) throws IOException {
Random random=new Random();
File file=new File(str);
if(!file.exists()){
try {
//如果不存在新建
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
//获取输出流
BufferedWriter write= FileUtils.getWrite(str);
char[]ans=new char[]{'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
for (int i = 0; i <200000000 ; i++) {
StringBuilder sb=new StringBuilder();
//随机从字符数组中读取数据 每个字符串最多占16个字节
for (int j = 0; j < random.nextInt(16) ; j++) {
sb.append(ans[random.nextInt(ans.length)]);
}
if(sb.length()==0){
//空的不写入
continue;
}
write.write(sb.toString());
write.newLine();
}
//关闭输出流,写入到文件中
FileUtils.closeWriter(write);
}
//创建2048个txt小文件
public static void createText(String str){
for (int i = 0; i < 2048; i++) {
File f=new File(str+"\\"+i+".txt");
try {
f.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 把大文件拆分成2048个小文件
* @param bigPath 大文件路径
* @param minDir 小文件目录
*/
public static void split(String bigPath,String minDir) throws IOException {
//读取小文件目录
File file=new File(minDir);
//遍历所有小文件
File[] files = file.listFiles();
//获取输入流 读取大文件中的数据
BufferedReader reader=FileUtils.getReader(bigPath);
for (File f : files) {
//获取输出流 写到小文件中
BufferedWriter writer=FileUtils.getWrite(f.getPath());
int size=0;
String res=null;
//只要不为空 就一直读取文件中的内容
while ((res=reader.readLine())!=null){
size+=res.length();
//写入文件
writer.write(res);
//换行
writer.newLine();
//如果大于512kb结束 当前文件,开始新的文件写入
if(size>=512*1024){
break;
}
}
//关闭输出流 把缓冲区的内容到文件中
FileUtils.closeWriter(writer);
}
//关闭输入流
FileUtils.closeReader(reader);
}
/**
* 排序小文件并重写
* @param minDir 小文件目录
*/
public static void sortMin(String minDir) throws IOException {
//读取小文件目录
File file=new File(minDir);
//遍历所有小文件
File[] files = file.listFiles();
for (File f : files) {
//获取输入流 读取文件中的数据
BufferedReader reader=FileUtils.getReader(f.getPath());
List<String> list=new ArrayList<>();
String res=null;
while ((res=reader.readLine())!=null){
list.add(res);
}
//关闭输入流
closeReader(reader);
if(CollectionUtils.isEmpty(list)){
//如果这个文件没有数据 就不管他
continue;
}
//对每个小文件排序
Collections.sort(list);
//重新写回小文件
BufferedWriter writer=getWrite(f.getPath());
for (String s : list) {
writer.write(s);
writer.newLine();
}
//关闭输出流
closeWriter(writer);
}
}
/**
* 合并小文件到大文件
* @param bigPath 大文件路径
* @param minDir 小文件目录
*/
public static void merger(String minDir,String bigPath) throws IOException {
//读取小文件目录
File file=new File(minDir);
//遍历所有小文件
File[] files = file.listFiles();
//使用小顶堆 越小的字符越在前 小顶堆的长度就是文件的长度
PriorityQueue<BufferSort>p=new PriorityQueue<>(files.length, new Comparator<BufferSort>() {
@Override
public int compare(BufferSort o1, BufferSort o2) {
return o1.getStr().compareTo(o2.getStr());
}
}) ;
for (File f : files) {
//把文件流 放入小顶堆中
BufferedReader reader=getReader(f.getPath());
BufferSort bufferSort=new BufferSort(reader);
if(bufferSort.hashNext()){
//如果有数据 放入小顶堆中
p.add(bufferSort);
}else {
//如果没有数据 关闭流
bufferSort.close();
}
}
//获取输出流
BufferedWriter writer=FileUtils.getWrite(bigPath);
while (p.size()>0){
//从堆顶拿出来数据 比较
BufferSort bufferSort=p.poll();
//因为之前已经缓存了,所以可以拿到这一行的数据
writer.write(bufferSort.getStr());
writer.newLine();
if(bufferSort.hashNext()){
//有数据 继续放入小顶堆中 对比下一行数据
p.add(bufferSort);
}else {
//没有数据 关闭输入流
bufferSort.close();
}
}
//最后关闭输出流
closeWriter(writer);
}
/**
* 打印前100个词频最高的单词
* @param path
*/
public static void top100(String path) throws IOException {
//因为是打印100个词频最高的单词,所以小顶堆的长度就是100
PriorityQueue<Pair>p=new PriorityQueue<>(100, new Comparator<Pair>() {
@Override
public int compare(Pair o1, Pair o2) {
//越小的在前
return o1.getNum()-o2.getNum();
}
});
BufferedReader reader=getReader(path);
//当前字符串
String curr=null;
//前一个字符串
String prev=null;
//前一个单词的数量
int prevNum=0;
while ((curr=reader.readLine())!=null){
if(prev==null){
prev=curr;
prevNum=1;
}else if(prev.equals(curr)){
prevNum++;
}else {
//如果前一个单词 和当前不一样
//判断小顶堆中是否满100
if(p.size()<100){
//如果没满直接放入 前一个
p.add(new Pair(prev,prevNum));
}else if(p.peek().getNum()<prevNum){
//如果大于100 判断堆顶的元素是否 小于前一个数量
//如果比前一个小,那么移除堆顶的元素
p.remove();
//放入前一个数据
p.add(new Pair(prev,prevNum));
}
//更新前一个单词和前一个单词的数量
prev=curr;
prevNum=1;
}
}
int index=0;
while (p.size()>0){
index++;
//把堆中的数据 都打印出来
Pair pair=p.poll();
System.out.println(pair.getWord()+"-次数--"+pair.getNum()+"--索引:"+index);
}
}
}
package com.example.demo.entity;
import java.io.BufferedReader;
import java.io.IOException;
//用于流排序的类
//可以缓存当前流中的内容
//判断下一行有没有数据
public class BufferSort {
private BufferedReader reader;
private String str;
public BufferSort(BufferedReader reader){
this.reader=reader;
}
public String getStr() {
return str;
}
//判断下一行有没有数据 然后把当前的结果缓存起来 用于小顶堆排序比较
public boolean hashNext(){
try {
str=reader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return str!=null;
}
//关闭输入流
public void close(){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.example.demo.mapper;
public class Pair {
private String word;
private Integer num;
public Pair(String word,Integer num){
this.num=num;
this.word=word;
}
public Integer getNum() {
return num;
}
public String getWord() {
return word;
}
}
package com.example.demo.mapper;
import com.example.demo.entity.FileUtils;
import java.io.IOException;
public class Test2 {
public static void main(String[] args) throws IOException {
FileUtils.generate("data\\danci.txt");
System.out.println("生成1个G单词成功");
FileUtils.createText("dc");
System.out.println("创建2048个空文件成功");
FileUtils.split("data\\danci.txt","dc");
System.out.println("写入2048个小文件成功");
FileUtils.sortMin("dc");
System.out.println("排序每个小文件成功");
FileUtils.merger("dc","data\\da.txt");
System.out.println("合并小文件成功");
System.out.println("打印词频最高的100个单词");
FileUtils.top100("data\\da.txt");
}
}
数据出现0kb的也不用管他,总数据是够的
我们的字符串是存储的512kb,但是txt还有自己的元数据,所以这里看到是772kb