大数据位图法(无重复排序,重复排序,去重复排序,数据压缩)之Java实现
1,位图法介绍
位图的基本概念是用一个位(bit)来标记某个数据的存放状态,由于采用了位为单位来存放数据,所以节省了大量的空间。举个具体的例子,在Java中一般一个int数字要占用32位,如果能用一位就表示这个数,就可以缩减大量的存储空间。一般把这种方法称为位图法,即Bitmap。
位图法比较适合于判断是否存在这样的问题,元素的状态比较少,元素的个数比较多的情况之下。那么具体咋么做呢,这样,非常简单明了就是,2.5亿个整数里面,我维护一个长度等于最大整数值得字符串,每个整数是否存在我就在该整数对应的位置置为1,比如,有{2, 4, 5, 6, 67, 5}这么几个整数,我维护一个 00…0000 67位的字符串。但是,如果你不知道整数的最大值,你至少需要一个长度2^32的字符串,因为整数的最大值就是2^32,(int占4个字节,因此是32位),那这就最少是512M内存,从char的长度算内存会算吧,直接、最大整数/8*2^20 就是M的单位。那这么说来就可以理解位图法了。
2,BitSet
正因为位图运算在空间方面的优越性,很多语言都有直接对它的支持。如在C++的STL库中就有一个bitset容器。而在Java中,在java.util包下也有一个BitSet类用来实现位图运算。此类实现了一个按需增长的位向量。BitSet的每一位都由一个boolean值来表示。用非负的整数将BitSet的位编入索引,可以对每个编入索引的位进行测试、设置或者清除。通过逻辑与、逻辑或和逻辑异或操作,可以使用一个BitSet修改另一个BitSet的内容。
需要注意的是BitSet底层实现是通过一个long数组来保存数据的,也就是说它增长的最小单位是一个long所占的逻辑位,即64位。但如果不是对存储区空间有极致的要求,而且对自己的基本功非常有信心,不建议自己去实现一个跟BitSet类似的类来实现相关的功能。因为jdk中的类都是极精简并做过合理优化的,BitSet类比较长。
3,无重复排序
java JDK里面容器类的排序算法使用的主要是插入排序和归并排序,可能不同版本的实现有所不同,关键代码如下:
/**
* Performs a sort on the section of the array between the given indices
* using a mergesort with exponential search algorithm (in which the merge
* is performed by exponential search). n*log(n) performance is guaranteed
* and in the average case it will be faster then any mergesort in which the
* merge is performed by linear search.
*
* @param in -
* the array for sorting.
* @param out -
* the result, sorted array.
* @param start
* the start index
* @param end
* the end index + 1
*/
@SuppressWarnings("unchecked")
private static void mergeSort(Object[] in, Object[] out, int start,
int end) {
int len = end - start;
// use insertion sort for small arrays
if (len <= SIMPLE_LENGTH) {
for (int i = start + 1; i < end; i++) {
Comparable<Object> current = (Comparable<Object>) out[i];
Object prev = out[i - 1];
if (current.compareTo(prev) < 0) {
int j = i;
do {
out[j--] = prev;
} while (j > start
&& current.compareTo(prev = out[j - 1]) < 0);
out[j] = current;
}
}
return;
}
int med = (end + start) >>> 1;
mergeSort(out, in, start, med);
mergeSort(out, in, med, end); // merging // if arrays are already sorted - no merge
if (((Comparable<Object>) in[med - 1]).compareTo(in[med]) <= 0) {
System.arraycopy(in, start, out, start, len);
return;
}
int r = med, i = start; // use merging with exponential search
do {
Comparable<Object> fromVal = (Comparable<Object>) in[start];
Comparable<Object> rVal = (Comparable<Object>) in[r];
if (fromVal.compareTo(rVal) <= 0) {
int l_1 = find(in, rVal, -1, start + 1, med - 1);
int toCopy = l_1 - start + 1;
System.arraycopy(in, start, out, i, toCopy);
i += toCopy;
out[i++] = rVal;
r++;
start = l_1 + 1;
} else {
int r_1 = find(in, fromVal, 0, r + 1, end - 1);
int toCopy = r_1 - r + 1;
System.arraycopy(in, r, out, i, toCopy);
i += toCopy;
out[i++] = fromVal;
start++;
r = r_1 + 1;
}
} while ((end - r) > 0 && (med - start) > 0); // copy rest of array
if ((end - r) <= 0) {
System.arraycopy(in, start, out, i, med - start);
} else {
System.arraycopy(in, r, out, i, end - r);
}
}
下面我们说下位图法排序的思路:其实思路开篇已经交代,为了让大家更容易理解,我将通过举例的方式进一步阐明,假设我们有一个不重复的整型序列{n1, n2, ... ,nn},假设最大值为nx,则我们可以维护一个长度为nx的位串,第一遍遍历整个序列,将出现的数字在位串中对应的位置置为1;第二遍遍历位图,依次输出值为1的位对应的数字,这些1所在的位串中的位置的索引代表序列数据,1出现的先后位置则代表序列的大写。
下面按上面的原理用Java实现:
package acm; import java.util.*; public class javaUniqueSort {
public static int[] temp = new int[100001];
public static List<Integer> tempList = new ArrayList<Integer>();
public static int count ;
public static long start ;
public static long end ; public static List<Integer> uniqueSort(final List<Integer> uniqueList) {
javaUniqueSort.tempList.clear();
for (int i = 0; i < javaUniqueSort.temp.length; i++) {
javaUniqueSort.temp[i] = 0;
}
for (int i = 0; i < uniqueList.size(); i++) {
javaUniqueSort.temp[uniqueList.get(i)] = 1;
}
for (int i = 0; i < javaUniqueSort.temp.length; i++) {
if (javaUniqueSort.temp[i] == 1) {
javaUniqueSort.tempList.add(i);
}
} return javaUniqueSort.tempList;
} public static void getStartTime() {
javaUniqueSort.start = System.nanoTime();
} public static void getEndTime(final String s) {
javaUniqueSort.end = System.nanoTime();
System.out.println(s + ": " + (javaUniqueSort.end - javaUniqueSort.start) + "ns");
} public static void main(final String[] args) { List<Integer> firstNum = new ArrayList<Integer>();
List<Integer> secondNum = new ArrayList<Integer>(); for (int i = 1; i <= 100000; i++) {
firstNum.add(i);
secondNum.add(i);
} Collections.shuffle(firstNum);
Collections.shuffle(secondNum); getStartTime();
Collections.sort(firstNum);
getEndTime("java sort run time "); getStartTime();
secondNum = uniqueSort(secondNum);
getEndTime("uniqueSort run time "); }
}
执行结果
4,有重复排序
有重复的整数序列排序,分为两种情况,保留重复的整数排序,和去除重复整数排序。
4.1 保留重复的整数排序
思路:上面讲述了无重复的整数序列排序,其实序列中的整数在位串中只用两个状态,要么在序列中出现(1),要么不出现(0),而对于有重复的整数序列,我们仍然可以用序列中整数出现的次数来表示数据状态,只是现在这个状态的数目是不确定的。实现方式也上面类似。
package acm; import java.util.*; public class javaDuplicateSort {
public static List<Integer> tempList = new ArrayList<Integer>();
public static int count;
public static long start ;
public static long end ; public static void main(final String[] args) {
Random random = new Random();
List<Integer> firstNum = new ArrayList<Integer>();
List<Integer> secondNum = new ArrayList<Integer>(); for (int i = 1; i <= 100000; i++) {
firstNum.add(i);
secondNum.add(i);
firstNum.add(random.nextInt(i + 1));
secondNum.add(random.nextInt(i + 1));
}
Collections.shuffle(firstNum);
Collections.shuffle(secondNum); getStartTime();
Collections.sort(firstNum);
getEndTime("java sort run time "); getStartTime();
secondNum = uniqueSort(secondNum);
getEndTime("uniqueSort run time "); } public static List<Integer> uniqueSort(final List<Integer> uniqueList) {
javaDuplicateSort.tempList.clear();
int[] temp = new int[200002];
for (int i = 0; i < temp.length; i++) {
temp[i] = 0;
}
for (int i = 0; i < uniqueList.size(); i++) {
temp[uniqueList.get(i)]++;
}
for (int i = 0; i < temp.length; i++) {
for (int j = temp[i]; j > 0; j--) {
javaDuplicateSort.tempList.add(i);
}
} return javaDuplicateSort.tempList;
} public static void getStartTime() {
javaDuplicateSort.start = System.nanoTime();
} public static void getEndTime(final String s) {
javaDuplicateSort.end = System.nanoTime();
System.out.println(s + ": " + (javaDuplicateSort.end - javaDuplicateSort.start) + "ns");
}
}
执行结果:
4.2 去除重复整数排序
思路:去重的意思就是整数序列中多次出现的整数只保留一次,这也很好处理,可以对上面的方法再往前推一步,对位串中大于1的数全部置1,这样就把重复的数据给去除了(或者在排序的时候增设一个条件状态数大于1的,按1来处理,这样也能得到想要的结果),方法很多,看个人的喜好,这里我就不去实现了。
5,数据压缩
假设有这样一份数据,记录了全国1990-1999年出生的人的姓名和出生年月的键值对。假设正好有一千万人,那就要存储一千万个姓名和年份。如何运用Bitmap的思想来压缩数据呢。下面提供几种思路。从人的角度来看,由于一共就只有10个年份,可以用4个bit将它们区分开。如0000表示1990年,1001表示1999年。那一个人的出生年份就可以用4个bit位来表示,进而一千万个年份就可以压缩为一千万个4位的bit组;从另一个角度来看这个问题,我们有10个年份,每个人要么是要么不是在这个年份出生。每个人对于年份来说就可以抽象为一个bit位,所以我们可以把一千万的年龄压缩为10个一千万位的bit组。这样压缩的力度不如按人的角度压缩的大,但从年份出发的问题会有一定的优势,如有哪些人是1990年出生的,只需遍历1990年对应的那个bit组就可以了。可以看出来不管从哪个角度,bitmap的压缩都是建立在数据中存在大量的冗余数据的基础上的,如年份。而在上面的问题中,年份的分布是散乱的,那假如我们事先把数据进行了排序,把相同的出生年份的人排在一起,那数据就可以进一步压缩。这样一来就只要记录每个年份的人数,就可以根据下标来判断每个人的出生年份。
总结
位图法可以用于海量数据排序,海量数据去重,海量数据压缩,针对于稠密的数据集可以很好体现出位图法的优势(内存消耗少,速度较快),但对于稀疏数据集,应用位图法反而会适得其反,比如我们有一个长度为10的序列,最大值为20亿,则构造位串的内存消耗将相当大250M,而实际却只需要40个字节,此外位图法还存在可读性差等缺点。
参考文献:
https://jinfagang.gitlab.io/2017/09/01/%E4%B8%87%E5%8F%98%E4%B8%8D%E7%A6%BB%E5%85%B6%E5%AE%97%E4%B9%8B%E6%B5%B7%E9%87%8F%E6%95%B0%E6%8D%AE%E4%B8%8B%E7%9A%84%E7%AE%97%E6%B3%95%E9%97%AE%E9%A2%98%E5%A4%84%E7%90%86%E6%80%9D%E8%B7%AF/
http://blog.csdn.net/u013291394/article/details/50211181
http://blog.csdn.net/y999666/article/details/51220833
http://blog.csdn.net/korey_sparks/article/details/52512870
大数据位图法(无重复排序,重复排序,去重复排序,数据压缩)之Java实现的更多相关文章
- mysql优化----大数据下的分页,延迟关联,索引与排序的关系,重复索引与冗余索引,索引碎片与维护
理想的索引,高效的索引建立考虑: :查询频繁度(哪几个字段经常查询就加上索引) :区分度要高 :索引长度要小 : 索引尽量能覆盖常用查询字段(如果把所有的列都加上索引,那么索引就会变得很大) : 索引 ...
- Oracle查询字符串数据进行排序,以及去重复
原本的的一张表,填写数据的字段为字符串varchar2类型,然后进行排序的时候,就会出现问题.会默直接默认判断为第一个数字9最大,而不判断整个数字的大小. 所以,就要用到TO_NUMBER函数 sel ...
- 大数据高可用集群环境安装与配置(04)——安装JAVA运行环境
Hadoop运行在java环境,所以在安装Hadoop之前,需要安装好jdk 提前下载好jdk安装包(jdk-8u161-linux-x64.tar.gz),将它上传到指定的安装目录当中,然后运行安装 ...
- 杂记- 3W互联网的圈子,大数据敏捷BI与微软BI的前端痛点
开篇介绍 上周末参加了一次永洪科技在中关村 3W 咖啡举行的一次线下沙龙活动 - 关于它们的产品大数据敏捷 BI 工具的介绍.由此活动,我想到了三个话题 - 3W 互联网的圈子,永洪科技的大数据敏捷 ...
- 如何基于Go搭建一个大数据平台
如何基于Go搭建一个大数据平台 - Go中国 - CSDN博客 https://blog.csdn.net/ra681t58cjxsgckj31/article/details/78333775 01 ...
- shell入门-uniq去重复和tee重定向
命令:uniq 选项:-c 显示重复数量 说明:去重复,不sort多个功能,显示几个重复 命令:tee 说明:重定向加上双重输出 [root@wangshaojun ~]# cat 2.txt1222 ...
- BAT推荐免费下载JAVA转型大数据开发全链路教程(视频+源码)价值19880元
如今随着环境的改变,物联网.AI.大数据.人工智能等,是未来的大趋势,而大数据是这些基石,万物互联,机器学习都是大数据应用场景! 为什么要学习大数据?我们JAVA到底要不要转型大数据? 好比问一个程序 ...
- Kona JDK 在腾讯大数据领域内的实践与发展
导语 | 近日,云+社区技术沙龙“腾讯开源技术”圆满落幕.本次沙龙邀请了多位腾讯技术专家,深度揭秘了腾讯开源项目TencentOS tiny.TubeMQ.Kona JDK.TARS以及Medical ...
- 大数据hadoop面试题2018年最新版(美团)
还在用着以前的大数据Hadoop面试题去美团面试吗?互联网发展迅速的今天,如果不及时更新自己的技术库那如何才能在众多的竞争者中脱颖而出呢? 奉行着"吃喝玩乐全都有"和"美 ...
随机推荐
- 转载SQL_trace 和10046使用
SQL_TRACE是Oracle提供的用于进行SQL跟踪的手段,是强有力的辅助诊断工具.在日常的数据库问题诊断和解决中,SQL_TRACE是非常常用的方法.本文就SQL_TRACE的使用作简单探讨,并 ...
- matplotlib绘制柱状图
参考自Matplotlib Python 画图教程 (莫烦Python)(11)_演讲•公开课_科技_bilibili_哔哩哔哩 https://www.bilibili.com/video/av16 ...
- python的数据类型的有序无序
列表有序可变 字典无序不可变 元组不可变 集合无序不可变 数字不可变 字符串不可变
- Java基础知识陷阱(三)
本文发表于本人博客. 之前都讲了有关字符串的陷阱,那今天来说下关于静态这个东西,这分为静态变量.静态方法,先看下面的代码请问结果输出是什么?: class Person01{ private stat ...
- 【转载】IDEA:放置型塔防备忘录
下周开始做原型了,我需要再次细细的整理一遍设计思路,确保每一个设计都能为了我所追求的玩家体验添砖加瓦,而不是互相打架.同时本文还能提供最原始的VISION,待到将来开发万一陷入泥淖,翻出此文来可以起到 ...
- WWDC 2017 苹果开发者大会
1.更新了系统固件 iOS 11 macOS High Sierra watchOS 4 tvOS 11 2.更新了硬件以及新设备 升级了 iMac 以及 iMac Pro 升级了 MacBook ...
- SiteMesh使用(2.4.2)
SiteMesh是一个网页布局和修饰的框架.我理解的是在一个母版页上引入页面各个位置的信息,从而拼接成一个页面展示出来.它定义了一个过滤器,把页面统一加上头部和底部. 我的项目是在springmvc中 ...
- Chrome搜索设置
https://encrypted.google.com/ncr https://encrypted.google.com/search?{google:RLZ}{google:acceptedSug ...
- oracle中修改表已有数据的某一列的字段类型的方法,数据备份
1.在开发过程中经常会遇到表中的某一个字段数据类型不对,比如说需要保存的数据带小数,但是在最初设计的时候是给的number(10)类型,开始保存是整数的时候满足要求,后来在保存小数的时候 会发现自动四 ...
- yum安装memchache
转载地址:http://www.cnblogs.com/jiunadianshi/articles/2001334.html 标准的CentOS5软件仓库里面是没有memcache相应的包的,所以,我 ...