本文章也同步至本人的CSDN博客中: http://blog.csdn.net/u012881584/article/details/70477832

今天来说一个Java中处理大文本字符串虑重的两个解决方案。

相信大家在实际工作中都遇到过数据重复的问题, 当然也就存在虑重的工作。

比如数据库中需要对同一个字段进行虑重, 大多数情况下我们直接使用Set就能解决问题, 今天我所说的这个大文本虑重是什么含义呢?一起来看看需求吧。

需求:

公司SEO人员给了我一个文本文件, 里面大概有三千多万行字符串, 他们的要求是希望我用最短的时间把这个文本文件重复的给删除掉。 起初我想的直接用excle去处理吧, 当时 因为这个文件都达到了几百兆, 所以编辑修改起来都很费劲。

这里直接給出解决思路:

首先脑海中想到的第一个就是用大数据去处理, 只是耳边经常听过Hadoop,Spark之类的词, 但是自己也并未真正接触过。于是便一通Google, 然后找到两个解决方案。

  • 利用布隆过滤器去解决。
  • 利用Spark的distinct去解决。

1, 布隆过滤器

  • 原理

    如果想判断一个元素是不是在一个集合里,一般想到的是将集合中所有元素保存起来,然后通过比较确定。链表、树、散列表(又叫哈希表,Hash table)等等数据结构都是这种思路。但是随着集合中元素的增加,我们需要的存储空间越来越大。同时检索速度也越来越慢。

Bloom Filter 是一种空间效率很高的随机数据结构,Bloom filter 可以看做是对 bit-map 的扩展, 它的原理是:

当一个元素被加入集合时,通过 K 个 Hash 函数将这个元素映射成一个位阵列(Bit array)中的 K 个点,把它们置为 1。检索时,我们只要看看这些点是不是都是 1 就(大约)知道集合中有没有它了:

如果这些点有任何一个 0,则被检索元素一定不在;

如果都是 1,则被检索元素很可能在。

  • 优点

    It tells us that the element either definitely is not in the set or may be in the set.

    它的优点是空间效率和查询时间都远远超过一般的算法,布隆过滤器存储空间和插入 / 查询时间都是常数O(k)。另外, 散列函数相互之间没有关系,方便由硬件并行实现。布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势。

  • 缺点

    但是布隆过滤器的缺点和优点一样明显。误算率是其中之一。随着存入的元素数量增加,误算率随之增加。但是如果元素数量太少,则使用散列表足矣。

(误判补救方法是:再建立一个小的白名单,存储那些可能被误判的信息。)

另外,一般情况下不能从布隆过滤器中删除元素. 我们很容易想到把位数组变成整数数组,每插入一个元素相应的计数器加 1, 这样删除元素时将计数器减掉就可以了。然而要保证安全地删除元素并非如此简单。首先我们必须保证删除的元素的确在布隆过滤器里面. 这一点单凭这个过滤器是无法保证的。另外计数器回绕也会造成问题。

这里只是简单做个介绍, 有兴趣的盆友可以参考:更多布隆过滤器简介

  • 代码示例:
public static void main(String[] args) throws Exception {
final BloomFilter<String> dealIdBloomFilter = BloomFilter.create(new Funnel<String>() {
@Override
public void funnel(String from, PrimitiveSink into) {
into.putString(from, Charsets.UTF_8);
}
//0.0000001d为错误率, 9000000 为预估元素的个数, 我第一次测试用了大概9000000行字符串的文本
}, 9000000, 0.0000001d);
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(new File("C:\\Users\\WangMeng\\Desktop\\test.txt")), "utf-8"));
String line;
int i = 0;
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
final boolean put = dealIdBloomFilter.put(line);
if (put) {
sb.append(line).append("\n");
i++;
}
if (i % 1000 == 0) {
//保存虑重后的文本。
FileUtils.write(new File("C:\\Users\\WangMeng\\Desktop\\Java类\\seo\\bloomFilterSplit.txt"),
sb.toString(), Charsets.UTF_8, true);
sb = new StringBuilder();
}
}
}

使用BloomFilter,有三个重要的值,错误率(false positive rate)、哈希函数个数以及BloomFilter位数组的大小,关于这三个值的最优配置算法,相关阅读中的文章有详细的说明。有一个原则,(BloomFilter位数组大小)/(实际的元素个数)越大,错误率越低,但消耗的空间会越多.

2, 使用Spark过滤大文本文件

使用或者说接触Spark是因为公司有人做过一次这个方面的分享, 所以有些耳熟, 于是便从网上找了些入门按理, 自己尝试着用了一下。

  • 使用Spark首先需要在pom文件中引入spark-core包
<!-- https://mvnrepository.com/artifact/org.apache.spark/spark-core_2.11 -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>2.1.0</version>
</dependency>
public static void main(String[] args) throws Exception {
System.setProperty("hadoop.home.dir", "C:\\Users\\WangMeng\\Desktop\\Java类\\hadoop-common-2.2.0\\");
SparkConf conf = new SparkConf().setAppName("Text String Distinct").setMaster("local").set("spark.executor.memory", "1g");
JavaSparkContext sc = new JavaSparkContext(conf);
//读取需要虑重的文本文件
JavaRDD<String> textFile = sc.textFile("C:\\Users\\WangMeng\\Desktop\\Java类\\seo\\test.txt");
final JavaRDD<String> distinct = textFile.distinct();
final long count = distinct.count();
//保存率重后的文本文件
distinct.coalesce(1).saveAsTextFile("C:\\Users\\WangMeng\\Desktop\\Java类\\seo\\sparkSplit.txt");
}

用Spark是不是很简单?代码也很少, 只需要读取文本创建一个rdd, 然后使用distinct就可以了, 如果想了解更多可以查看:Spark更多介绍

在windows下这里好像好需要一个hadoop-common-2.2.0包, 如果不引入会报找不到winutils.exe, 这里提供一个下载地址, 如果不能下载了请联系我。hadoop-common-2.2.0下载地址

结语

到了这里就讲完了, 当然, 对于大文本的处理还是有更多更好的方法的,我这里只是尝试了这两种方案, 处理千万级行的数据都不用一分钟就可以虑重好, 布隆过滤器和Spark过滤后的行数都是相差无几的, 这里我还是更推荐使用Spark, 毕竟现在比较流行大数据, 有时间我也会继续探究大数据的相关内容。

【Java】大文本字符串滤重的简单方案~的更多相关文章

  1. JAVA里的字符串,String 类简单介绍

    http://www.360doc.com/content/14/1107/23/17130779_423471141.shtml

  2. Java查询大文本

    但JAVA本身缺少相应的类库,需要硬编码才能实现结构化文件计算,代码复杂且可读性差,难以实现高效的并行处理. 使用免费的集算器可以弥补这一不足.集算器封装了丰富的结构化文件读写和游标计算函数,书写简单 ...

  3. Java调用SqlLoader将大文本导入数据库

    Java调用SqlLoader将大文本导入数据库 业务场景:将一千万条数据,大约500M的文本文档的数据导入到数据库 分析:通过Java的IO流解析txt文本文档,拼接动态sql实现insert入库, ...

  4. <JAVA - 大作业(1)文本编辑器 >

    <JAVA - 大作业(1)文本编辑器 > 背景 JAVA上机大作业:qq / 代码评价系统 第一次上机主题是练习JAVA自带的GUI图形化编程 目的:实现一个跟window10记事本界面 ...

  5. 13、如何拆分含有多种分隔符的字符串 14、如何判断字符串a是否以字符串b开头或结尾 15、如何调整字符串中文本的格式 16、如何将多个小字符串拼接成一个大的字符串

    13.如何拆分含有多种分隔符的字符串 import re s = "23:41:2314\1234#sdf\23;" print(re.split(r'[#:\;]+',s))   ...

  6. coding++:java—提取Html文本字符串中的内容

    package com.tree.ztree_demo; import java.util.regex.Matcher; import java.util.regex.Pattern; public ...

  7. Java中的字符串操作(比较String,StringBuiler和StringBuffer)

    一.前言 刚开始学习Java时,作为只会C语言的小白,就为其中的字符串操作而感到震撼.相比之下,C语言在字节数组中保存一个结尾的\0去表示字符串,想实现字符串拼接,还需要调用strcpy库函数或者自己 ...

  8. Java中的字符串常量池

    ava中字符串对象创建有两种形式,一种为字面量形式,如String str = "droid";,另一种就是使用new这种标准的构造对象的方法,如String str = new ...

  9. Java基础 -- 连接字符串时,使用+还是StringBuilder

    结论 1-源代码中使用的+连接,实际上都使用的是StringBuilder. 2-用jad工具反编译,好处之一就是可以同时生成字节码和源代码.这样可以进行对照研究. ----------------- ...

随机推荐

  1. Remove Element leetcode

    Given an array and a value, remove all instances of that value in place and return the new length. T ...

  2. loadrunner:判断是否服务器连接池瓶颈

    分析Web Resources中的Connections per second可以判断是否服务器连接池瓶颈. connections per second会给出两种不同状态的连接数:中断的连接和新建的 ...

  3. Raft算法,从学习到忘记

    Raft算法,从学习到忘记 --Raft算法阅读笔记. --Github 概述 说到分布式一致性算法,可能大多数人的第一反应是paxos算法.但是paxos算法一直以来都被认为是难以理解,难以实现.S ...

  4. Archlinux 的U盘自动装载(二)升级到 udisks2

    在安装 mysql-gui-tools 过程中,发现需要用到 udisks2.udisks和udisks2可以互相替换也可以共用.最后决定换用 udisks2 安装 先卸载原来的软件,如果有的话. p ...

  5. wxWidgets 的 Linux / Windows 混合开发环境

    目标 建立一个开发 GUI 交互界面程序的环境,要求: 以 C/C++ 为主要程序设计语言. 最好能充分利用已有的 MFC 开发经验. 以 Linux 为主要开发环境.可以在 Windows 中编译并 ...

  6. subline text3常用插件介绍

    常用插件介绍:  html beautify(ctrl+shift+alt+f) 自动排版代码 Emmet 输入少量代码后摁Tab键,系统自动补全代码. AutoFileName 快速列出你想引用的文 ...

  7. JavaWeb之Servlet总结

    今天上班居然迟到了,昨天失眠了,看完吐槽大会实在不知道做些什么,刚好朋友给我发了两个JavaWeb的练习项目,自己就又研究了下,三四点才睡,可能周日白天睡的太多了,早上醒来已经九点多了,立马刷牙洗脸头 ...

  8. 基于C#的UDP通信(使用UdpClient实现,包含发送端和接收端)

    UDP不属于面向连接的通信,在选择使用协议的时候,选择UDP必须要谨慎.在网络质量令人十分不满意的环境下,UDP协议数据包丢失会比较严重.但是由于UDP的特性:它不属于连接型协议,因而具有资源消耗小, ...

  9. Jmeter 正则提取器

    背景: 用户购买商品,为防止CSRF攻击,在购买请求参数中要携带token参数,token的值是随机加密处理的,每次登录值都会刷新 场景: 用户登录站点,访问商品列表,购买商品 脚本设计: 1.登录 ...

  10. CSS后代选择器、子元素选择器、相邻兄弟选择器区别与详解

    派生选择器用的很多,派生选择器具体包括为后代选择器.子元素选择器.相邻兄弟选择器,我们来理解一下他们之间的具体用法与区别. 1.css后代选择器语法:h1 em {color:red;} 表示的是从h ...