k1和k2映射到同一值域,也叫压缩映射,比如通排序就是一种特殊的哈希表
直接定址法
数字分析法
平方折中法
折叠法
随机数法
除留余数法 ×最好理解
开放定址法
拉链法 就是后面写一个链表继续下去,但是如果数据很多可能会退化成0(n)
双散列 如果这里冲突就顺着这个往下走0(1)
再散列 再次哈希,一直到找到空的为止 0(1)
在java中先实现hashmap再实现hashset,,起始就是hashmap是的
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
/*
* @desc HashMap测试程序
*
* @author skywang
*/
public class test2 {
public static void main(String[] args) {
testHashMapAPIs();
}
private static void testHashMapAPIs() {
// 初始化随机种子
Random r = new Random();
// 新建HashMap
HashMap map = new HashMap();
// 添加操作
map.put("one", r.nextInt(10));
map.put("one", r.nextInt(10));
map.put("two", r.nextInt(10));
map.put("three", r.nextInt(10));
// 打印出map
System.out.println("map:为" + map); //打印map
// 通过Iterator遍历key-value
Iterator iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
System.out.println("next : " + entry.getKey() + " - " + entry.getValue());
}
// HashMap的键值对个数
System.out.println("size:" + map.size());
// containsKey(Object key) :是否包含键key
System.out.println("contains key two : " + map.containsKey("two"));
System.out.println("contains key five : " + map.containsKey("five"));
// containsValue(Object value) :是否包含值value
System.out.println("contains value 0 : " + map.containsValue(0));
// remove(Object key) : 删除键key对应的键值对
map.remove("three");
System.out.println("map:" + map);
// clear() : 清空HashMap
map.clear();
// isEmpty() : HashMap是否为空
System.out.println((map.isEmpty() ? "map is empty" : "map is not empty"));
}
}
定义一个接口
package org.lanqiao.algo.elementary._10_hash;
import java.util.Iterator;
public interface IMap {
/*清除所有键值对*/
void clear();
/*key是否已经存在*/
boolean containsKey(K key);
/*value是否存在*/
boolean containsValue(V value);
/*根据key获得value*/
V get(K key);
/*map是否为空*/
boolean isEmpty();
/*所有key组成的数组*/
MyHashSet keySet();
/*存入键值对*/
void put(K key, V value);
/*把另外一个map中的所有键值对存入到当前map中*/
void putAll(IMap extends K, ? extends V> map);
/*根据key删除一个键值对*/
V remove(K key);
/*键值对的个数*/
int size();
/*所有的value组成的数组*/
V[] values();
Iterator iterator();
}
写一个类调用这个接口并且将他完整化
package org.lanqiao.algo.elementary._10_hash;
import java.util.Iterator;
public class MyHashMap implements IMap {
private int length = 16;
private Node[] buckets = new Node[length];//桶
private int size;
@Override
public void clear() {
for (int i = 0; i < buckets.length; i++) {
buckets[i] = null;
}
}
@Override
public boolean containsKey(K key) {
int index = hash1(key);
if (buckets[index] == null) {
return false;
} else {
Node p = buckets[index];//相当于在链表中找key
while (p != null) {
K k1 = p.key;
//借用java机制,hashcode和equals都来自于Object,用户可以改写这两个方法——制定对象相等的规则
if (k1 == key || (k1.hashCode() == key.hashCode() && k1.equals(key))) {
return true;
}
p = p.next;
}
}
return false;
}
@Override
public boolean containsValue(V value) {
for (int i = 0; i < buckets.length; i++) {
if (buckets[i] != null) {
Node p = buckets[i];
while (p != null) {
if (p.value.equals(value))
return true;
}
}
}
return false;
}
@Override
public V get(K key) {
int index = hash1(key);
if (buckets[index] == null) {
return null;
} else {
Node p = buckets[index];
while (p != null) {
K k1 = p.key;
if (k1 == key || (k1.hashCode() == key.hashCode() && k1.equals(key))) {
return p.value;
}
p = p.next;
}
}
return null;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public MyHashSet keySet() {
MyHashSet set = new MyHashSet<>();
for (int i = 0; i < buckets.length; i++) {
if (buckets[i] != null) {
Node p = buckets[i];
while (p != null) {
set.add(p.key);
p = p.next;
}
}
}
return set;
}
@Override
public void put(K key, V value) {
Node node = new Node<>(key, value);
int index = hash1(key);//算出在桶中的位置
if (buckets[index] == null) {//桶中没有东西
buckets[index] = node;
size++;
} else {
Node p = buckets[index];//链表的表头找到
while (p != null) {
K k1 = p.key;
if (key == k1 || key.hashCode() == k1.hashCode() && key.equals(k1)) {
p.value = value;//存在相同的key,则更新value
break;
}
if (p.next == null) {
p.next = node;
size++;
break;
}
p = p.next;
}
}
}
private int hash1(K key) {
// return key.hashCode() % length;
int h = 0;
int seed = 31;//素数
String s = key.toString();
for (int i = 0; i != s.length(); ++i) {
h = seed * h + s.charAt(i);
}
return h % length;
}
@Override
public void putAll(IMap extends K, ? extends V> map) {
}
@Override
public V remove(K key) {
int index = hash1(key);//先定桶的位置
if (buckets[index] == null) {
return null;
} else {
Node p = buckets[index];//找到表头
Node pre = p;
while (p != null) {
K k1 = p.key;
if (k1.hashCode() == key.hashCode() && k1.equals(key)) {
//移除
if (p == pre) {
buckets[index] = pre.next;
} else {
pre.next = p.next;
}
size--;
return p.value;
}
pre = p;
p = p.next;
}
}
return null;
}
@Override
public int size() {
return size;
}
@Override
public V[] values() {
return null;
}
private class MapInterator implements Iterator {
int i = 0;
Node p = buckets[0];
@Override
public boolean hasNext() {
while (this.i < length && p == null) {
this.i++;
if (this.i == length)
p = null;
else
p = buckets[this.i];
}
//i是一个非空的桶,p是链表头
return p != null;
}
@Override
public Node next() {
Node res = p;
p = p.next;
return res;
}
}
@Override
public Iterator iterator() {
return new MapInterator();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < buckets.length; i++) {
if (buckets[i] != null) {
Node p = buckets[i];
while (p != null) {
sb.append("(" + p.key + "," + p.value + "),");
p = p.next;
}
}
}
return sb.toString();
}
public class Node {
public K key;
public V value;
public Node(K key, V value) {
this.key = key;
this.value = value;
}
private Node next;
@Override
public String toString() {
return "BSTNode{" +
"key=" + key +
", value=" + value +
'}';
}
}
}
hashset的话存的时候也只存一个数,整体功能比较少
package org.lanqiao.algo.elementary._10_hash;
import java.util.Iterator;
public interface IHashSet {
void add(E key);
void remove(E key);
void clear();
boolean contains(E key);
boolean isEmpty();
int size();
Iterator iterator();
}
package org.lanqiao.algo.elementary._10_hash;
import java.util.Iterator;
public class MyHashSet implements IHashSet {
private MyHashMap map = new MyHashMap<>();
@Override
public void add(E key) {
map.put(key, null);
}
@Override
public void remove(E key) {
map.remove(key);
}
@Override
public void clear() {
map.clear();
}
@Override
public boolean contains(E key) {
return map.containsKey(key);
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public int size() {
return map.size();
}
@Override
public Iterator iterator() {
Iterator iter = map.iterator();
return new Iterator() {
@Override
public boolean hasNext() {
return iter.hasNext();
}
@Override
public E next() {
return (E) iter.next().key;
}
};
}
@Override
public String toString() {
Iterator iterator = map.iterator();
StringBuilder sb = new StringBuilder();
while (iterator.hasNext()) {
sb.append(iterator.next().key + ",");
}
return sb.toString();
}
}
在工业中应用比较多,用于判断hashset和hanshmap里有没有一个东西,主要针对海量东西中查找一个元素是否在一个集合中,
原理是:当一个元素加入一个集合中,通过k个散列函数将这个元素映射成一个位数组中的k个点,把他们设为1,检索时,我们只需要看看这些点是不是都是1就知道集合中有没有他了,如果这些点有任何一个0,则被检查元素一定不再,如果都是1,则很有可能在
这里代码我有不太懂是因为不知道问什么要写8个hash函数???
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
/**简化版本的布隆过滤器的实现*/
public class test2 {
public static final int NUM_SLOTS = 1024 * 1024 * 8;//位图的长度
public static final int NUM_HASH = 8;//hash函数的个数,一个hash函数的结果用于标记一个位
private BigInteger bitmap = new BigInteger("0");//位图
public static void main(String[] args) {
//测试代码
test2 bf = new test2();
ArrayList contents = new ArrayList<>();
contents.add("sldkjelsjf");
contents.add("ggl;ker;gekr");
contents.add("wieoneomfwe");
contents.add("sldkjelsvrnlkjf");
contents.add("ksldkflefwefwefe");
for (int i = 0; i < contents.size(); i++) {
bf.addElement(contents.get(i));
}
System.out.println(bf.check("sldkjelsvrnlkjf"));
System.out.println(bf.check("sldkjelnlkjf"));
System.out.println(bf.check("ggl;ker;gekr"));
}
/**将message+n映射到0~NUM_SLOTS-1之间的一个值*/
private int hash(String message, int n) {
message = message + String.valueOf(n);
try {
MessageDigest md5 = MessageDigest.getInstance("md5");//将任意输入映射成128位(16个字节)整数的hash函数
byte[] bytes = message.getBytes();
md5.update(bytes);//update函数,,转化为字节
byte[] digest = md5.digest();
BigInteger bi = new BigInteger(digest);//至此,获得message+n的md5结果(128位整数)
return Math.abs(bi.intValue()) % NUM_SLOTS;//可能会超出位图的长度
} catch (NoSuchAlgorithmException ex) {
Logger.getLogger(test2.class.getName()).log(Level.SEVERE, null, ex);
}
return -1;
// return (int)Math.abs(HashFunctions.bernstein(message,NUM_SLOTS));
}
/*处理原始数据
* 1.hash1(msg)标注一个位…… hash的值域0~NUM_SLOTS-1
* */
public void addElement(String message) {
for (int i = 0; i < NUM_HASH; i++) {//为了简便,这里并没有真的去写8个hash函数,而是下面那个函数传入不同的i,函数就会不一样
int hashcode = hash(message, i);//代表了hash1,hash2……hash8
//结果,用于标注位图的该位为1
if (!bitmap.testBit(hashcode)) {//如果还不为1 //testbit函数代表查看啊某一位是否为1
//标注位图的该位为1
bitmap = bitmap.or(new BigInteger("1").shiftLeft(hashcode));
}
}
}
public boolean check(String message) {
for (int i = 0; i < NUM_HASH; i++) {
int hashcode = hash(message, i);
//hashcode代表一个位置
if (!this.bitmap.testBit(hashcode)) {//有一个是0,就返回false
//如果位图的该位为0,那么message一定不存在
return false;
}
}
return true;//不精确,有可能误判
}
}
有一个包含20亿个全是32位整数的大文件,在其中找到出现次数最多的数
tip:一个字节(byte)是八个比特(bit)
思路:32位,一个字节=8位,所以32位是4个byte(4个字节) 用map的话key和value都是32位整数,意思就是一条数据是8个字节,,那么20*8 =160亿个字节,10亿个字节是一个G ,那么他就需要16G,,有时候提上会说内存限制不能超过2个G,,那么就要用哈希分流,思路是把20亿个分成16个文件,然后每个文件去找亿个top1,,最后拿16个top1 去比大小就可以了
思路一:可以像上面那个一样,分成很多小文件,这样一个一个的查,效率的确会低,因为要查40亿多次,,然后有一点点不确定的地方是只要一个小文件的containskey(x)没有,那其他的也一定没有????
思路二:2^10 * 2^10 *2^10 等于一个G 再乘2^2 就是4个G,,这是4个G的比特(bit) 再除以8是0.5个G的字节,即500mb(兆)的字节,,然后我去建立一个40亿的bitmap就可以了,去扫描标记就OK
思路:假设每一个url占64个byte那就是6400亿,就是640G,这样一定不行
所以这里可以采取上面那种小文件,但是生成小文件时不可随意分割而是哈希散列,保证不同的情况一定出现在同一文件中而非不同文件,否则无法判断重复否,, topk问题也就是同时维护640个100的小顶堆,,生成之后比大小