1GB单词,限制1mb内存,怎么取词频最高的前100个

我这边生成单词是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

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值