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
- HashSet源码阅读
- Java源码阅读-HashSet
- JDK源码阅读之HashSet
- Java源码阅读之Hashset
- Java集合源码阅读笔记-HashSet
- 【JDK源码阅读10-util】Set接口---HashSet
- HashSet源码
- HashSet源码
- HashTable & HashSet 源码分析
- hashset 源码解析
- HashSet源码分析
- HashSet源码分析
- 【Java源码】HashSet、LinkedHashSet
- HashSet 源码分析
- HashSet源码分析
- HashSet源码解析
- JDK源码-HashSet
- hashset源码导读
- MySQL 数据库 备份与恢复
- 最熟悉的陌生人:ListView 中的观察者模式
- 构建shadowsocks服务器并局域网共享
- ECMAScript6笔记:Iterator和for...of循环
- centos之lnmp
- HashSet源码阅读
- Guava笔记Lists
- Java RMI 框架(远程方法调用)
- 敏捷开发系列学习总结(8)——创业公司研发团队建设
- 关于SVN提交不成功问题
- MySQL——存储过程和函数使用总结
- SAP新总账 凭证分割
- 公式编辑器编辑双向斜箭头的教程
- pthread_cleanup_push()/pthread_cleanup_pop()