- 浏览: 130783 次
- 性别:
文章分类
最新评论
-
seacow2008:
同1楼,深入浅出
Java并发编程(二) CountDownLatch -
Mojarra:
java0000wa 写道不能搞得通俗易懂一点? demo S ...
fastupload 0.6.0发布 -
java0000wa:
不能搞得通俗易懂一点? demo Spring jar包都少了 ...
fastupload 0.6.0发布 -
Mojarra:
tegger 写道不好意思设置字符编码解决了,还是挺好用的,不 ...
fastupload 0.6.0发布 -
tegger:
不好意思设置字符编码解决了,还是挺好用的,不错
fastupload 0.6.0发布
前几天在论坛里看到一个帖子说百度的一道面试题,两个文件里各约有两亿行数据,每行只有一个数字,问如何求两个文件中数据的交集。
最近对大数据的处理比较感兴趣,所有思考了一下这个问题,对于JVM来说,两亿数据是非常多的,直接用数组来处理,是行不通的,另外,两亿的数据,效率也是一个重要的考量度。本来可以借助Hash的方法来解决这个问题,但因为每行只有一个数据,也就是只有数字0~9, 那么可以采用一个简便的方法,读取文件中的数字,假如是1, 那么给一个int数组的第1位加1,其他的数字如此类推,假如是2,给这个int数组的第2位加1, 直到所有的数据读取完毕。另外一个文件也采用相同的方法,最后,比较这两个int数组的相同位置上的元素,取当中小的作为交集的个数,比如a[9] = 100, b[9]=80, 那么说明在这两个文件中,有80个数字9是有交集的。最终的交集结果,用这样的统计形式表示,
{0 -- 1000}, 数字0在两个文件中有交集,并且是1000次
{1 -- 999}
....
{8 -- 2000}
{9 -- 80}
基本的思路有了,问题是如何读取两亿个数字呢?即使数据采用分行方式保存,用readLine方法读取,两个文件,总共要读取4亿次,效率肯定是很低的,必须要用buffer,分析buffer,得出数字。从题目本身来说,一行只有一个数字是很好分析buffer的,考量到一定的扩展,本程序假定一行是一个数据,给出一个通用的解析方法。
首先定义一个基本的类,很简单,定义数据量,buffer的大小,和数据的最大值,比如10,表示文件里的数据是从0~9, 如果是100, 则是0~99。1000, 则表示0~999,这个最大值也决定了读取数据到目标数组的最大长度。
public class Data { int counter = 200000000; int bs = 0x80000; int max = 10; }
DataFinder类,从两个文件输入流中读取数据并且统计到数组,并求交集。因为考虑到每次读取到buffer中的bytes不一定是0x0a结尾,需要考虑这个0x0a在下一次读取的buffer中的情况,需要对上次结尾的一些bytes和本次读取的头几个bytes进行合并。取到一组正确的表示一个数据的bytes后,先转换成String,在转换成integer,根据这个integer来给数组中的一个特定位增加计数器。后来证明,这个思路中,需要花精力去处理的是IO及分析数据部分。
package data.comparison; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; public class DataFinder extends Data { private int[] readData(InputStream is) throws IOException { int[] ds = new int[max]; byte[] buff = new byte[bs]; int bc = 0; byte[] delta = null; while ((bc = (is.read(buff))) != -1) { int p = 0; int i = 0; if (delta != null) { while (buff[i++] != 0x0a) { } int len = i - p - 1; byte[] bn = new byte[delta.length + len]; System.arraycopy(delta, 0, bn, 0, delta.length); System.arraycopy(buff, 0, bn, delta.length, len); p = i; int n = Integer.parseInt(new String(bn)); ds[n]++; delta = null; } while (i < bc & p < bc) { if (buff[i] == 0x0a || i == bc - 1) { int len = i == bc - 1 ? bc - 1 - p: i - p; byte[] bn = new byte[len]; if (len==0){ System.out.println(); } System.arraycopy(buff, p, bn, 0, len); int n = Integer.parseInt(new String(bn)); ds[n]++; p = i + 1; } i++; } if (i == bc && buff[i - 1] != 0x0a) { delta = new byte[i - p]; System.arraycopy(buff, p, delta, 0, i - p); } } return ds; } public void compare(InputStream in1, InputStream in2) throws IOException { long t = System.currentTimeMillis(); StringBuilder result = new StringBuilder(32); int[] ds1 = this.readData(in1); int[] ds2 = this.readData(in2); for (int i = 0; i < ds1.length & i < ds2.length; i++) { result.append(String.format("{number: %d, occur: %d}%n", i, ds1[i] < ds2[i] ? ds1[i] : ds2[i])); } System.out.println(result.toString()); System.out.println("--- done ---"); System.out.format("comparison costs %d ms%n", System.currentTimeMillis() - t); } public static void main(String args[]) throws IOException { InputStream in1 = new FileInputStream("file1"); InputStream in2 = new FileInputStream("file2"); new DataFinder().compare(in1, in2); } }
最后,再写一个随即创造数据的类,
package data.comparison; import java.io.FileOutputStream; import java.io.IOException; import java.util.Random; public class DataCreator extends Data { public void random(String file) throws IOException { long t = System.currentTimeMillis(); FileOutputStream writer = new FileOutputStream(file); int c = 0; while (c < counter) { Random random = new Random(); StringBuilder builder = new StringBuilder(200000); for (int i = 0; i < bs & c < counter; i++, c++) { builder.append(random.nextInt(max)).append("\n"); } writer.write(builder.toString().getBytes()); } writer.flush(); writer.close(); System.out.format("cost %d ms%n", System.currentTimeMillis() - t); } /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { DataCreator dc = new DataCreator(); dc.random("file1"); dc.random("file2"); } }
在I5 2.3G/4G/5400转的硬盘/Mac OS 10.7/JDK6.0.29的软硬件环境上,使用buffer方式后,性能还是比较可以的,随机写两个文件,只需要10秒不到的时间,可以接受。
cost 9313 ms cost 8884 ms
求两个文件的交集,共花了63秒
{number: 0, occur: 20001948} {number: 1, occur: 20000771} {number: 2, occur: 19996245} {number: 3, occur: 19996415} {number: 4, occur: 19997276} {number: 5, occur: 19992915} {number: 6, occur: 19997709} {number: 7, occur: 19997344} {number: 8, occur: 19995041} {number: 9, occur: 19998757} --- done --- comparison costs 63365 ms
这个方法把众多的数据转换成一个较小的数组,采用计数器的方法来求交集,算法上很简练,同时也避免了JVM heap开销过大,在读取数据时,另辟蹊径,采用buffer读文件,再从buffer中分析数据,从而避免了IO的问题。 那么,对于这个面试题,还有没有更好的思路呢?
[全文完]
发表评论
-
Fastupload 0.6.1 发布
2014-03-03 09:44 15600.6.1版本主要修复了JQuery-form提交ajax请 ... -
fastupload 0.6.0发布
2013-06-23 18:24 1771Fastupload 0.6.0完善或者新增加的功能有: ... -
uProfiler使用指南
2013-06-13 14:43 1552简介: uProfiler Community是面向主题 ... -
uProfiler Community 1.0发布
2013-06-08 09:39 1986uProfiler Community 1.0是面 ... -
fastupload-springmvc 0.5.5发布
2013-04-15 21:55 1999fastupload-springmvc是利用f ... -
Fastupload 0.5.3发布
2013-01-05 19:55 991相对于以往的版本,fastupload 0.5.3做出了明 ... -
fastupload已发布至maven中心库
2012-11-29 09:44 1026为了让大家更方便的使用fastupload开源项目,fastu ... -
白话MVC(五)初窥Spring MVC
2012-11-22 21:17 2244在 为 struts2 项 目写完 fastuplo ... -
Fastupload 0.4.7发布,支持struts2
2012-10-28 20:56 1756Fastupload 0.4.7这个版本中主要增加了支持str ... -
白话MVC(四)为Struts2编写文件上传插件
2012-10-28 20:47 3158Struts2中,在Dispatcher.java ... -
Fastupload 0.4.2发布
2012-10-19 12:05 1542更新:fastupload 0.4.2支 ... -
fastupload召集开源开发志愿者
2012-10-11 19:57 98fastupload开源项目自发布0.3.5版本后,文件上传的 ... -
白话MVC(三)Struts2拦截器巧妙装配Model Bean
2012-10-12 18:02 2030白话MVC(二) 在Struts的过滤器中,经过调用Prep ... -
白话MVC(二)Struts2中Model的处理基础-ActionContext
2012-10-09 13:17 2433白话MVC(一) ... -
白话MVC(一)Model的产生及处理
2012-09-29 00:36 34644白话MVC(二) 最近在带一“徒弟”,领悟能力很高,对我 ... -
fastupload API开发快速上手
2012-09-01 16:36 2881fastupload提供两种从multipart/form-d ... -
文件上传的秘密(五)0.31版本功能基本完备
2012-08-26 21:15 1391fastupload 0.31版本上周已经发布,因为工作的关系 ... -
fastupload 0.3.1发布
2012-08-21 15:25 1723fastupload根据RFC 1867文档 ... -
开源项目fastupload 0.2.3发布
2012-07-06 17:19 1996fastupload 0.2.3发布,增加了对sub-boun ... -
文件上传的秘密(四)大小限制与进度
2012-05-28 14:27 8403RFC1867规范中,对表单上传文件的大小和进度都没有作出规定 ...
相关推荐
1. 给你 A,B 两个文件,各存放 50 亿条 URL,每条 URL 占用 64 字节,内存限制是 4G,让你找出 A,B 文件共同的 URL。 2. 海量日志数据,提取出某日访问百度次数最多的那个 IP。 3. 如何根据输入元素个数 n,确定位数...
Bloom Filter是一种用于检验元素是否在一个集合中的概率型数据结构,特别适用于大数据环境下进行快速判断元素是否存在的情况,尤其在数据字典构建、数据去重或集合交集计算方面表现出色。其基本原理是利用位数组和多...
**适用范围**:数据字典、判重、集合求交集等场景。 **优化建议**:根据输入元素个数n,合理设定位数组m的大小及hash函数数量k,以达到最小的错误率。通常情况下,k = (ln2)*(m/n),错误率不大于E时,m ≥ n*lg(1/E...
**适用范围**:Bloom Filter常用于实现数据字典、数据判重或集合求交集等功能。 **基本原理**: - 使用一个位数组和多个独立的哈希函数来实现。 - 将元素通过哈希函数映射到位数组中的特定位置,并将这些位置标记为1...
Bloom filter是一种常见的大数据处理方法,适用范围包括:实现数据字典、进行数据的判重、或者集合求交集。其基本原理是使用位数组+k个独立的hash函数,将hash函数对应的值的位数组置1,查找时如果发现所有hash函数...
- 给定两个文件A和B,各存放50亿条URL,每条URL占用64字节。在内存限制为4GB的情况下,找出A和B文件中的共同URL。可以通过构建Bloom Filter来降低内存消耗,尽管这可能会导致误报率的增加。 #### 二、Hash **定义*...
布隆过滤器主要用于实现数据字典、数据去重以及集合求交集等场景。 **基本原理及要点:** - **核心结构:** 位数组 + K个独立哈希函数 - **操作流程:** 将数据通过K个哈希函数映射到位数组中,若数据存在则将其...
在国内市场,DLP(数据丢失防护)和数据库安全是最大的两个子市场,市场规模均超过10亿元。 数据安全需求的增长主要源于数据量的爆炸性增长、多样化的数据使用场景、数据交易需求的增加以及数据互联的需求。预计到...
7. 上亿数据统计出现次数最多的 N 个数据: 使用优先队列(最小堆)或者 Top-K 算法,每次新数据出现时更新堆。 8. 判断一个数是否在 40 亿个数中: 类似于第6题,可以构建布隆过滤器,但考虑到误判率要求更低,...
5. **找出两个文件的共同url** 利用布隆过滤器(Bloom Filter)对每个文件的url进行哈希处理,先判断是否存在交集,若没有交集则直接返回。若有交集,再采用更精确的方法,如Sort-Intersection或Hash Join,找出...
集合操作如`SADD`用于添加元素,`SMEMBERS`用于查看集合的所有成员,`SDIFF`, `SINTER`, `SUNION`则分别用于计算两个或更多集合的差集、交集和并集。由于集合内部使用哈希表实现,因此添加、删除和查找操作的时间...
这种算法广泛应用于搜索引擎、推荐系统、数据库索引等领域,能够有效地处理十亿级别的数据量。在实际应用中,还需要考虑如何适应动态数据变化、节省存储空间以及并行处理等问题,这往往需要结合其他数据结构和算法来...
- **HTML5游戏与原生游戏用户重叠度**:数据显示,原生游戏与HTML5游戏用户之间的重叠度达到了59%,这表明两者之间存在显著的交集。此外,原生游戏用户与HTML5游戏用户的比例为5:3,显示出HTML5游戏在用户群体中的...
在高维空间中,数据点通常由多个特征(维度)来表示,例如,在一个包含2亿个“天空物体”的目录中,每个物体的辐射特性可以通过7个不同的频率带(维度)来描述。聚类的目标就是将这些物体根据它们的特性分成若干类别...
1.给定两个文件1,分别由100亿个query,我们只有1G内存,如何找到两个文件的交集,分别给出精确算法和近似算法 2.如何扩展BloomFilter使得他支
持久化有 RDB(快照)和 AOF(Append Only File)两种方式,确保在服务重启后能够恢复数据。主从复制保证数据的冗余和高可用性,而 Redis 集群则可以实现数据的分布式存储和负载均衡。 在实际应用中,Redis 可以...
将多个集合合并成没有交集的集合,可以通过并查集或哈希表等数据结构实现,使得每个集合中的元素互不相同。 平面内11个点连成48条直线,根据组合数学中的组合公式C(n, 2) = n*(n-1)/2计算得出;而可以连成的三角形...