HashSet源码阅读

来源:互联网 发布:3ds数据搬家 编辑:程序博客网 时间:2024/06/04 19:56

HashSet实现了集合类的接口,可以进行排重处理。但是与数组类似,集合存储Java对象时,并不是存储真正的对象,而是存储对象的引用,这些引用指向真正的Java对象。
项目中涉及到长周期的UV统计,需要用HashSet存储占用较大的内存,实现了一个较为节约内存的mini-space-set。
区别是:HashSet用的Hash方法是链式寻址法,mini-space-set用到的是开放寻址法

把mini-space-set的代码贴上:

package com.bj58.realtime.util;public class mini_space_set {    private int max_len = 0;    private int element_len = 0;    private int num = 0;    private int hash_size = 16 * 1024 * 1024; //16 M    private int hash_half_size = hash_size / 2;    private int hash_mask = hash_size - 1; //掩码    private byte[][] hash = null;//这个二维数组的作用是    public mini_space_set(int max_len) {        this.max_len = max_len;//规定的最长的长度        this.element_len = this.max_len + 1;        this.hash = new byte[hash_size][element_len]; //1 * 16M * 40 = 640M     }    public boolean contains(String str) {        if (is_blank(str) || str.length() > max_len){            System.out.println("strOutLength: " + str.length());            return false; //需要存的最长的字节数        }        int n = str.length();        String blanks = "";        for (; n < max_len; n++) blanks = blanks + " ";        str = str + blanks;        int code = str.hashCode();        int hash_pos = code & hash_mask; //与之后的结果是什么        byte p[] = hash[hash_pos];        if (p[0] != 0 && !item_equals(str, hash_pos)) {            final int inc = ((code >> 8) + code) | 1;            do {                code += inc;                hash_pos = code & hash_mask;                p = hash[hash_pos];            } while (p[0] != 0 && !item_equals(str, hash_pos));        }        if (p[0] == 1) return true;        else return false;    }    public boolean add(String str) {        if (is_blank(str) || str.length() > max_len){            System.out.println("strOutLength: " + str.length());            return false;         }        int n = str.length();        String blanks = "";        for (; n < max_len; n++) blanks = blanks + " ";        str = str + blanks;        //System.out.println("str: " + str + str.length());        int code = str.hashCode();//hash算法,返回一个整型的数值        //System.out.println("code: " + code);//这里是个负数        int hash_pos = code & hash_mask;//将负数变为正数        //System.out.println("hash_pos: " + hash_pos);        byte p[] = hash[hash_pos];//确定hash索引的桶,这个桶使用byte[]的数组//      for(byte b:p){//          System.out.println(b);//通过这里的输出看出来 都是0,也就是没有存过数//      }        if (p[0] != 0 && !item_equals(str, hash_pos)) { //确定是否hash冲突的标志            //如果冲突了            final int inc = ((code >> 8) + code) | 1;//下一个桶的位置,步长的计算方式            do {                code += inc;                hash_pos = code & hash_mask;                p = hash[hash_pos];            } while (p[0] != 0 && !item_equals(str, hash_pos));//这个就是一直在找        }        if (p[0] == 0) {            hash[hash_pos][0] = 1;//数组中的第一个位置是标志位            byte cid_bytes[] = str.getBytes();            System.arraycopy(cid_bytes, 0, hash[hash_pos], 1, max_len);            num++;            if (num == hash_half_size) rehash();//超过一半的位置被填满了 就要rehash            return true;        } else return false;    }    public int size() {        return num;    }    public void clear() {        hash = null;    }    private boolean item_equals(String str, int index) { //输入的字符串与第几个桶的对比        if (index >= (hash_size)) {            throw new RuntimeException("unimpossible index error");        }        String val = new String(hash[index], 1, max_len);//从1的索引开始确定已经存在的字符        //System.out.println("val: " + val);        return str.equals(val);//返回要存入的字符是否与现有的字符相同    }    private void rehash() {        final int new_mask = 2 * hash_size - 1;        byte new_hash[][] = new byte[2 * hash_size][element_len];        for (int i = 0; i < hash_size; i++) {            byte p[] = hash[i];            if (p[0] != 0) { //把已经有的数据重新计算一遍                String cid = new String(hash[i], 1, max_len);                int code = cid.hashCode();                int hash_pos = code & new_mask;                if (new_hash[hash_pos][0] != 0) {                    final int inc = ((code >> 8) + code) | 1;                    do {                        code += inc;                        hash_pos = code & new_mask;                    } while (new_hash[hash_pos][0] != 0);                }                new_hash[hash_pos][0] = 1;                System.arraycopy(hash[i], 1, new_hash[hash_pos], 1, max_len);            }        }        hash_mask = new_mask;        hash = new_hash;        hash_half_size = hash_size;        hash_size = 2 * hash_size;    }    private static boolean is_blank(String str) {        return str == null || str.trim().equals("");    }    public static void main(String args[]) throws Exception {        mini_space_set set = new mini_space_set(100);//new 一个这个set需要很久么,初始化的时候的容量        System.out.println("已占用内存: " + Runtime.getRuntime().totalMemory()/1024/1024 + "M"); //一个set需要的内存大概是1G        //set.add("1");//      mini_space_set set2 = new mini_space_set(10);//      System.out.println("已占用内存: " + Runtime.getRuntime().totalMemory()/1024/1024 + "M"); //一个这个需要的是1452M        for (int i = 0; i < 5000000; i++) { //按照10W的容量            set.add(String.format("%70d", i));//这样格式化之后是多长了        }        System.out.println("已占用内存_after: " + Runtime.getRuntime().totalMemory()/1024/1024 + "M");        /*for (int i = 0; i < 10000000; i++) {            if (!set.contains(String.format("%32d", i))) {                System.out.println("error:" + i);                break;            }        }        for (int i = 10000000; i < 20000000; i++) {            if (set.contains(String.format("%32d", i))) {                System.out.println("error:" + i);                break;            }        }*/    }}

与HashSet的源码进行比较:

HashSet的所有实现都是基于HashMap,所有的基本操作也是在hashMap的基础上进行,至于为什么这样,需要进一步理解。

0 0