如何在有限内存下对外部大文件进行排序

文章目录

  • 引言
  • 外部排序算法简介
  • 实现步骤
    • 步骤 1: 分割文件
    • 步骤 2: 合并已排序的文件
  • 示例代码
  • 关键点解析
  • 结论

引言

在大数据时代,处理海量数据成为许多应用的常见需求。然而,当数据量超过内存容量时,传统的内存排序算法将不再适用。本文将介绍如何使用Java编程语言,在有限的内存条件下(例如2GB内存)对一个10GB的文本文件进行排序。
这也是一个最近几年比较流行的实际面试题:10GB的文本文件,全是介于[10000000, 88888888]之间的随机整数,每行一个数字。要求使用2GB内存,编程对该文件中的数字排序并按降序重新输出到一个新的文件中。
我们将使用外部排序思想来解决这个问题。

外部排序算法简介

外部排序算法是一种用于处理无法完全加载到内存中的数据集的排序方法。最常用的外部排序算法是多路归并排序,它分为两个主要步骤:分割 和 合并。

  • 分割:将大文件分割成多个小文件,每个小文件的大小应小于可用内存,以便可以对其进行内部排序。
  • 合并:将所有已排序的小文件合并成一个大的已排序文件。

基于外部排序思想,我们把文件分成79个,分别存储10、11、12、……、87、88开头的数字,平均每个文件大小约为130MB(10GB/79)。由于数字在文本中以字符串形式存储,再加上换行符,所以单行大小9bytes(8bytes+1byte),单个文件的数字个数约为1510_0000个。
把单个文件中的全部数字加载到内存中,每个数字是一个int类型的,占用4字节,总共占用57.6MB。
关于数字排序,这里使用位数组+HashMap计数的方式进行,提升排序效率。

实现步骤

步骤 1: 分割文件

首先,我们将大文件分割成多个小文件,每个小文件的大小不超过可用内存。
顺序读取10GB文件中的每一个数字n,则该数字应该写入的文件序号i = ( n / 100_0000 ) - 10;遍历完成后所有数字按照前两位分别存储到序号0-78的小文件中。

步骤 2: 合并已排序的文件

接下来,我们需要将所有小文件数字进行排序并合并成一个大的已排序文件。

  • 这里我们初始化一个长度为100_0000的位数组,分别用于表示当前文件中是否存在某个数字。同时还要初始化一个HashMap,用于存储每一个数字出现的次数。
  • 例如,现在处理序号0的文件,其数字范围是[1000_0000, 2000_0000),则位数组索引为0的位置表示文件0中是否存在数字1000_0000。
  • 逐个遍历该文件中的每一个数字,最后得到一个记录[1000_0000, 2000_0000)范围内每一个数字是否存在的数组和一个记录每一个数字出现次数的HashMap。
  • 从前向后遍历该位数组,并结合HashMap即可输出当前文件中所有数字的排序结果。
  • 从文件78到文件0逐个处理,并将结果输出到最终的结果文件中,排序完成。

示例代码

package org.hbin.sort;import java.io.*;
import java.util.*;/*** @author Haley* @version 1.0* 2024/10/25*/
public class SplitSort {/** 最小数字 */private static int min = 1000_0000;/** 最大数字 */private static int max = 8888_0000;/** 单个文件的数字个数 */private static int fileCapacity = 100_0000;/** 小文件数量 */private static int fileCount = max / fileCapacity - min / fileCapacity + 1;private static int bound = max - min + 1;/** 位数组 */private static boolean[] bits = new boolean[bound];/** 统计集合 */private static Map<Integer, Integer> map = new HashMap<>(bound);private static String fileName = "1MB.txt";private static String prefixBatchPath = "prefix_batch//";private static String prefixOutFileName = "prefix_merge_sort_";private static String outFileName;public static List<File> split() throws IOException {System.out.println("clean ...");File batchPath = new File(prefixBatchPath);if(batchPath.exists()) {batchPath.delete();}batchPath.mkdir();System.out.println("create batch files ...");List<File> files = new ArrayList<>();int offset = min / fileCapacity;BufferedWriter[] writers = new BufferedWriter[fileCount];for (int i = 0; i < fileCount; i++) {files.add(new File(prefixBatchPath + (i + offset)));writers[i] = new BufferedWriter(new FileWriter(prefixBatchPath + (i + offset)));}System.out.println("split file ...");long s = System.currentTimeMillis();long printCapacity = fileCapacity;try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {String line;int count = 0;while((line = reader.readLine()) != null) {int slot = Integer.parseInt(line) / fileCapacity;writers[slot - offset].write(line + System.lineSeparator());count ++;if(count % printCapacity == 0) {System.out.println("deal " + count);if(count / printCapacity >= 10) {printCapacity *= 10;}}}} catch (Exception e) {e.printStackTrace();}for (int i = 0; i < fileCount; i++) {writers[i].flush();writers[i].close();}System.out.println("split file. " + (System.currentTimeMillis() - s) / 1000 + " s.");return files;}public static void bitSort() throws IOException {// 清空outFileName = prefixOutFileName + fileName;File outFile = new File(outFileName);if(outFile.exists()) {outFile.delete();}outFile.createNewFile();long start = System.currentTimeMillis();int end = min / fileCapacity;for (int i = max / fileCapacity; i >= end; i--) {// 重置位数组Arrays.fill(bits, false);map.clear();int total = 0;int batch = 500_0000;File f = new File(prefixBatchPath + i);try (BufferedReader reader = new BufferedReader(new FileReader(f))) {String line;while((line = reader.readLine())!=null) {int num = Integer.parseInt(line);int index = num - min;if(!bits[index]) {bits[index] = true;map.put(num, map.getOrDefault(num, 0) + 1);}total ++;if(total % batch == 0) {System.out.println("deal ... " + total/10000 + "w, map's size: " + map.size());if(total > 2500_0000) {if(total % 100 == 0) {System.out.println("\tdeal ... " + total + ", map's size: " + map.size());}}}}}System.out.println(i  + " sort " + (System.currentTimeMillis() - start) / 1000 + " s.");print();}}public static void print() {long start = System.currentTimeMillis();try (BufferedWriter writer = new BufferedWriter(new FileWriter(outFileName, true))) {for (int i = bits.length - 1; i >= 0; i--) {if(bits[i]) {int num = i + min;for (int j = 0; j < map.get(num); j++) {writer.write(num + System.lineSeparator());}}}writer.flush();} catch (IOException e) {e.printStackTrace();}System.out.println("print " + (System.currentTimeMillis() - start) / 1000 + " s.");}public static void main(String[] args) throws IOException {
//        fileName = "200MB.txt"; // split-46s - bit 46s
//        fileName = "1GB.txt"; // 30+320=360s - 25+85=110s/25+65=93sfileName = "10GB.txt"; // - 300+200=550s/310+270=590slong start = System.currentTimeMillis();split();bitSort();System.out.println("total: " + (System.currentTimeMillis() - start) / 1000 + " s.");}
}

关键点解析

内存管理:通过拆分成不同的文件个数,确保单个文件大小不超过内存限制,最后排序加载单个文件时即可完成单个文件的数字排序。
文件操作:使用 BufferedReader 和 BufferedWriter 进行高效的文件读写操作。
排序:使用 位数组和Map 对每个小文件中的数据进行排序。
合并:按顺序处理每一个小文件,并最终合并输出到一个结果文件中。

结论

通过上述步骤,我们成功地在有限的内存条件下对一个10GB的文本文件进行了排序。这种方法不仅适用于本文中的场景,还可以扩展到其他需要处理大规模数据的应用中。希望本文能为您提供有价值的参考,帮助您在实际工作中解决类似的问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/469582.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《硬件架构的艺术》笔记(一):亚稳态

同步系统中如果数据和时钟满足建立保持时间的要求&#xff0c;不会发生亚稳态&#xff08;meastable&#xff09;。 异步系统中数据和时钟关系不固定&#xff0c;可能违反建立保持时间&#xff0c;就会输出介于两个有效状态之间的中间级电平&#xff0c;且无法确定停留在中间状…

统信UOS开发环境支持Electron

全面支持Electron开发环境,同时还提供了丰富的开发工具和开发资源,进一步提升工作效率。 文章目录 一、环境部署1. Electron应用开发介绍2. Electron开发环境安装安装Node.js和npm安装electron环境配置二、代码示例Electron开发案例三、常见问题一、环境部署 1. Electron应用…

动手学深度学习68 Transformer

1. Transformer 2. 多头注意力代码 通过不断地reshape&#xff0c;避免forloop操作。 什么样的shape进去&#xff0c;怎样的shape出来。 #save class MultiHeadAttention(nn.Module):"""多头注意力"""def __init__(self, key_size, query_size…

晨控RFID技术助力半导体制造业革新之路

晨控RFID技术助力半导体制造业革新之路 应用背景 随着信息技术的快速发展&#xff0c;无线射频识别技术逐渐成为连接物理世界与数字世界的桥梁。尤其在半导体产业中&#xff0c;RFID技术的应用不仅提升了生产效率&#xff0c;还加强了产品追踪与管理能力&#xff0c;对推动产…

ReactPress:构建高效、灵活、可扩展的开源发布平台

ReactPress Github项目地址&#xff1a;https://github.com/fecommunity/reactpress 欢迎Star。 在当今数字化时代&#xff0c;内容管理系统&#xff08;CMS&#xff09;已成为各类网站和应用的核心组成部分。ReactPress&#xff0c;作为一款融合了现代Web开发多项先进技术的开…

PyTorch版本的3D网络Grad-CAM可视化实验记录

前言 最近在跑代码的时候需要可视化一些网络中间层特征来诊断网络&#xff0c;但是我的backbone是一个3D网络&#xff0c;一般的Grad-CAM都是在2D网络中应用更广泛&#xff0c;查了一下也只有几篇博文是关于3D Grad-CAM的介绍的。自己参照他们的代码试了一下&#xff0c;但是可…

基于STM32的智能充电桩:集成RTOS、MQTT与SQLite的先进管理系统设计思路

一、项目概述 随着电动车的普及&#xff0c;充电桩作为关键基础设施&#xff0c;其智能化、网络化管理显得尤为重要。本项目旨在基于STM32微控制器开发一款智能充电桩&#xff0c;能够实现高效的充电监控与管理。项目通过物联网技术&#xff0c;提供实时数据监测、远程管理、用…

.NET中通过C#实现Excel与DataTable的数据互转

在.NET框架中&#xff0c;使用C#进行Excel数据与DataTable之间的转换是数据分析、报表生成、数据迁移等操作中的常见需求。这一过程涉及到将Excel文件中的数据读取并加载至DataTable中&#xff0c;以便于利用.NET提供的丰富数据处理功能进行操作&#xff0c;同时也包括将DataTa…

域名服务系统DNS (Domain Name System)

域名的介绍 熟悉了域名之后&#xff0c;不仅仅是应对考试&#xff0c;生活中看到一个常规的网址&#xff0c;我们也能快速想到这个网址对应的含义是什么&#xff0c;并且在记忆网址的时候也更加得心应手&#xff0c;快速了解域名中各个层级的含义&#xff0c;这是 非常有趣呢…

Kettle——CSV文件转换成excel文件输出

1.点击—文件—新建—转换 拖入两个组件&#xff1a; 按shift&#xff0b;鼠标左击建立连接&#xff0c;并点击主输出步骤&#xff0c; 点击CSV文件输入&#xff0c;选择浏览的csv文件&#xff0c;然后点击确定 同样&#xff0c;Excel也同上&#xff0c;只是要删除这个xls 并…

Select,poll,epoll和IO多路复用和NIO

Select&#xff0c;poll&#xff0c;epoll和IO多路复用和NIO IO 多路复用&#xff1a;是一种 I/O 处理机制&#xff0c;它允许单个线程同时处理多个 I/O 流&#xff08;如多个文件描述符对应的网络连接、文件操作等&#xff09;的输入输出操作&#xff0c;通过一种机制来监听这…

希尔排序(C语言)

一、步骤&#xff1a; 希尔排序的基本思想&#xff1a;先选定一个整数&#xff0c;把待排序文件中所有记录分成个组&#xff0c;所有距离为gap的记录分在同一组内&#xff0c;并对每一组内的记录进行排序。然后取重复上述分组和排序的工作。当到gap 1时&#xff0c;所有记录在…

自动驾驶为什么需要时间同步?高精度时间同步如何实现?

自动驾驶作为汽车与物联网技术、人工智能等高新技术融合的产物&#xff0c;具有新颖性、复杂性和巨大的挑战性。自动驾驶需要实时传输、处理海量数据&#xff0c;并实时做出决策&#xff0c;这不仅要求有通畅网络通信环境、强大的数据算力&#xff0c;更要求时间同步的超低时延…

【信号处理】基于联合图像表示的深度学习卷积神经网络

Combined Signal Representations for Modulation Classification Using Deep Learning: Ambiguity Function, Constellation Diagram, and Eye Diagram 信号表示 Ambiguity Function(AF) 模糊函数描述了信号的两个维度(dimensions):延迟(delay)和多普勒(Doppler)。 …

C++20 概念与约束(1)—— SFINAE

●《C20 概念与约束&#xff08;1&#xff09;—— SFINAE》 《C20 概念与约束&#xff08;2&#xff09;—— 初识概念与约束》 《C20 概念与约束&#xff08;3&#xff09;—— 约束的进阶用法》 1、从模板说起 众所周知&#xff0c;C在使用模板时&#xff0c;如果有多…

[极客大挑战 2019]PHP 1

[极客大挑战 2019]PHP 1 审题 猜测备份在www.zip中&#xff0c;输入下载文件。 知识点 反序列化 解题 查看代码 看到index.php中包含了class.php,直接看class.php中的代码 查看条件 当usernameadmin&#xff0c;password100时输出flag 构造反序列化 输入select中&#…

Kubebot:一款Google云平台下的Slackbot安全测试工具

Kubebot 今天给大家介绍的是一款名叫Kubebot的安全测试Slackbot&#xff0c;该工具基于Google 云平台搭建&#xff0c;并且提供了Kubernetes后端。 项目架构 数据流 1.API请求由Slackbot发起&#xff0c;发送至API服务器&#xff0c;API服务器以Kubernetes(K8s)集群中的Docke…

Kubernetes的基本构建块和最小可调度单元pod-0

文章目录 一&#xff0c;什么是pod1.1pod在k8s中使用方法&#xff08;1&#xff09;使用方法一&#xff08;2&#xff09;使用方法二 1.2pod中容器的进程1.3pod的网络隔离管理&#xff08;1&#xff09;pause容器的作用 1.4 Pod分类&#xff1a;&#xff08;1&#xff09;自主式…

Redis安装(Windows环境)

目录 1.下载2.双击安装后配置环境变量3.启动服务4.设置Windows服务5.启动客户端6.常用的Redis服务命令7.使用图形化界面工具查看Redis内部数据情况类似Navicat 连接数据库 1.下载 1.点击github下载地址 2.上方资源链接下载安装 2.双击安装后配置环境变量 3.启动服务 上图虽然…

windows下qt5.12.11使用ODBC远程连接mysql数据库

1、下载并安装mysql驱动,下载地址:https://dev.mysql.com/downloads/ 2、配置ODBC数据源,打开64位的ODBC数据源配置工具: