海量数据去重排序bitmap(位图法)在java中实现的两种方法

在海量数据中查找出重复出现的元素或者去除重复出现的元素是面试中常考的文图。针对此类问题,可以使用位图法来解决。例如:已知某个文件内包含若干个电话号码,要求统计不同的号码的个数,甚至在O(n)时间复杂度内对这些号码进行排序。

位图法需要的空间很少(依赖于数据分布,但是我们也可以通过一些放啊发对数据进行处理,使得数据变得密集),在数据比较密集的时候效率非常高。例如:8位整数可以表示的最大十进制数值为99999999,如果每个数组对应于一个bit位,那么把所有的八进制整数存储起来只需要:99Mbit = 12.375MB.

实际上,java jdk1.0已经提供了bitmap的实现BitSet类,不过其中的某些方法是jdk1.4之后才有的。

下面我先自己实现一下bitmap 的原理,然后再直接调用jdk的BitSet类分别实现bitmap, 方便比较理解:

package swordoffer;
//去除重复并排序
import java.util.Arrays;
import java.util.BitSet;
import java.util.Random;
/**
 * @author Gavenyeah
 * @date Time:
 * @des:
 */
public class BitMap {
  int ARRNUM = 800;
  int LEN_INT = 32;
  int mmax = 9999;
  int mmin = 1000;
  int N = mmax - mmin + 1;
  public static void main(String args[]) {
     new BitMap().findDuplicate();
    new BitMap().findDup_jdk();
  }
  public void findDup_jdk() {
    System.out.println("*******调用JDK中的库方法--开始********");
    BitSet bitArray = new BitSet(N);
    int[] array = getArray(ARRNUM);
    for (int i = 0; i < ARRNUM; i++) {
      bitArray.set(array[i] - mmin);
    }
    int count = 0;
    for (int j = 0; j < bitArray.length(); j++) {
      if (bitArray.get(j)) {
        System.out.print(j + mmin + " ");
        count++;
      }
    }
    System.out.println();
    System.out.println("排序后的数组大小为:" + count );
    System.out.println("*******调用JDK中的库方法--结束********");
  }
  public void findDuplicate() {
    int[] array = getArray(ARRNUM);
    int[] bitArray = setBit(array);
    printBitArray(bitArray);
  }
  public void printBitArray(int[] bitArray) {
    int count = 0;
    for (int i = 0; i < N; i++) {
      if (getBit(bitArray, i) != 0) {
        count++;
        System.out.print(i + mmin + "\t");
      }
    }
    System.out.println();
    System.out.println("去重排序后的数组大小为:" + count);
  }
  public int getBit(int[] bitArray, int k) {// 1右移 k % 32位 与上 数组下标为 k/32 位置的值
    return bitArray[k / LEN_INT] & (1 << (k % LEN_INT));
  }
  public int[] setBit(int[] array) {// 首先取得数组位置下标 i/32, 然后 或上
                    // 在该位置int类型数值的bit位:i % 32
    int m = array.length;
    int bit_arr_len = N / LEN_INT + 1;
    int[] bitArray = new int[bit_arr_len];
    for (int i = 0; i < m; i++) {
      int num = array[i] - mmin;
      bitArray[num / LEN_INT] |= (1 << (num % LEN_INT));
    }
    return bitArray;
  }
  public int[] getArray(int ARRNUM) {
    @SuppressWarnings("unused")
    int array1[] = { 1000, 1002, 1032, 1033, 6543, 9999, 1033, 1000 };
    int array[] = new int[ARRNUM];
    System.out.println("数组大小:" + ARRNUM);
    Random r = new Random();
    for (int i = 0; i < ARRNUM; i++) {
      array[i] = r.nextInt(N) + mmin;
    }
    System.out.println(Arrays.toString(array));
    return array;
  }
}

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。如果你想了解更多相关内容请查看下面相关链接

(0)

相关推荐

  • Java内存之happens-before和重排序

    happens-before原则规则: 程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作: 锁定规则:一个unLock操作先行发生于后面对同一个锁的lock操作: volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作: 传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C: 线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作: 线程中断规则:对线程interrup

  • 浅谈java指令重排序的问题

    指令重排序是个比较复杂.觉得有些不可思议的问题,同样是先以例子开头(建议大家跑下例子,这是实实在在可以重现的,重排序的概率还是挺高的),有个感性的认识 /** * 一个简单的展示Happen-Before的例子. * 这里有两个共享变量:a和flag,初始值分别为0和false.在ThreadA中先给 a=1,然后flag=true. * 如果按照有序的话,那么在ThreadB中如果if(flag)成功的话,则应该a=1,而a=a*1之后a仍然为1,下方的if(a==0)应该永远不会为 * 真,

  • JAVA中JVM的重排序详细介绍

    在并发程序中,程序员会特别关注不同进程或线程之间的数据同步,特别是多个线程同时修改同一变量时,必须采取可靠的同步或其它措施保障数据被正确地修改,这里的一条重要原则是:不要假设指令执行的顺序,你无法预知不同线程之间的指令会以何种顺序执行. 但是在单线程程序中,通常我们容易假设指令是顺序执行的,否则可以想象程序会发生什么可怕的变化.理想的模型是:各种指令执行的顺序是唯一且有序的,这个顺序就是它们被编写在代码中的顺序,与处理器或其它因素无关,这种模型被称作顺序一致性模型,也是基于冯·诺依曼体系的模型.

  • 海量数据去重排序bitmap(位图法)在java中实现的两种方法

    在海量数据中查找出重复出现的元素或者去除重复出现的元素是面试中常考的文图.针对此类问题,可以使用位图法来解决.例如:已知某个文件内包含若干个电话号码,要求统计不同的号码的个数,甚至在O(n)时间复杂度内对这些号码进行排序. 位图法需要的空间很少(依赖于数据分布,但是我们也可以通过一些放啊发对数据进行处理,使得数据变得密集),在数据比较密集的时候效率非常高.例如:8位整数可以表示的最大十进制数值为99999999,如果每个数组对应于一个bit位,那么把所有的八进制整数存储起来只需要:99Mbit

  • Java读取Map的两种方法与对比

    前言 在java中遍历Map有不少的方法.这篇文章我们就来看一下Java读取Map的两种方法以及这两种方法的对比. 一. 遍历Map方法A Map map = new HashMap(); Iterator iter = map.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = (Map.Entry) iter.next(); Object key = entry.getKey(); Object val = en

  • 浅谈java中String的两种赋值方式的区别

    类似普通对象,通过new创建字符串对象.String str = new String("Hello"); 内存图如下图所示,系统会先创建一个匿名对象"Hello"存入堆内存(我们暂且叫它A),然后new关键字会在堆内存中又开辟一块新的空间,然后把"Hello"存进去,并且把地址返回给栈内存中的str, 此时A对象成为了一个垃圾对象,因为它没有被任何栈中的变量指向,会被GC自动回收. 直接赋值.如String str = "Hello&

  • java中Object类4种方法详细介绍

    目录 Object(四大方法): hashCode()方法: equals()方法: getClass()方法: toString()方法: 总结 Object(四大方法): 文章干货满满,耐性看完~~何为Object?首先先来看看官方对Object的介绍:在这里附上Java官方的查阅工具:https://docs.oracle.com/en/java/javase/17/docs/api/index.html 由官方介绍可见,object属于Java.lang包内的一个类,而且提供了很多种方法

  • Java数组添加元素的两种方法

    目录 说在前面 方式一: 方式二: 说在前面 数组在使用前,长度就已固定,所以原数组长度是不能再改变了,基于此,提供如下两种方式,给数组添加数据.具体代码如下 方式一: 创建一个新数组,长度为原数组加1,然后将原数组数据添加到新数组,最后再添加需要的新数据 @Test public void redd111(){ String[] s1 = {"aaa","bbb","ccc"}; String[] s2 = new String[s1.leng

  • java创建线程的两种方法区别

    在Java中创建一个线程有两种方法:继承Thread类和实现Runnable接口. 下面通过两个例子来分析两者的区别: 1)继承Thread类 public class TestThread extends Thread { int count = 3; public TestThread(String ThreadName) { super(ThreadName); } @Override public void run() { for (int i = 0; i < 10; i++) if

  • java中ArrayList的两种排序方法实例

    目录 前言 1.ArrayList使用排序的初衷 2.对一个ArrayList中的数组进行排序. 3.多个ArrayList中的元素进行排序 总结 前言 由于其功能性和灵活性,ArrayList是 Java 集合框架中使用最为普遍的集合类之一.ArrayList 是一种 List 实现,它的内部用一个动态数组来存储元素,因此 ArrayList 能够在添加和移除元素的时候进行动态的扩展和缩减.你可能已经使用过 ArrayList,因此我将略过基础部分.如果你对 ArrayList 还不熟悉,你可

  • JAVA实现多线程的两种方法实例分享

    java语言已经内置了多线程支持,所有实现Runnable接口的类都可被启动一个新线程,新线程会执行该实例的run()方法,当run()方法执行完毕后,线程就结束了.一旦一个线程执行完毕,这个实例就不能再重新启动,只能重新生成一个新实例,再启动一个新线程. Thread类是实现了Runnable接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过Thread类的start()实例方法: 复制代码 代码如下: Thread t = new Thread(); t.start();

  • 使用java获取md5值的两种方法

    Message Digest Algorithm MD5(中文名为消息摘要算法第五版)为计算机安全领域广泛使用的一种散列函数,是一种比较常用的哈希算法. java中可以用两种方法实现,我们先说麻烦一点的,代码: 复制代码 代码如下: public class md5_test { //MD5的字符串常量 private final static String[] hexDigits = { "0", "1", "2", "3"

  • Java获取随机数的3种方法

    主要介绍了Java获取随机数的3种方法,主要利用random()函数来实现 方法1 (数据类型)(最小值+Math.random()*(最大值-最小值+1))例: (int)(1+Math.random()*(10-1+1)) 从1到10的int型随数 方法2 获得随机数 for (int i=0;i<30;i++) {System.out.println((int)(1+Math.random()*10));} (int)(1+Math.random()*10) 通过java.Math包的ra

随机推荐