Hash(散列函数)
定义
它是基于快速存取的角度设计的,也是一种典型的“空间换时间”的做法。顾名思义,该数据结构可以理解为一个线性表,但是其中的元素不是紧密排列的,而是可能存在空隙。
一个好的Hash函数应满足下列要求:
正向快速:给定明文和 hash 算法,在有限时间和有限资源内能计算出 hash 值。
逆向困难:给定(若干) hash 值,在有限时间内很难(基本不可能)逆推出明文。
输入敏感:原始输入信息修改一点信息,产生的 hash 值看起来应该都有很大不同。
冲突避免:很难找到两段内容不同的明文,使得它们的 hash 值一致(发生冲突)。即对于任意两个不同的数据块,其hash值相同的可能性极小;对于一个给定的数据块,找到和它hash值相同的数据块极为困难。
常用的Hash算法
MD4,MD5(被破解了)
SHA-1(被破解了),SHA-256,SHA-224,SHA-512,SHA-384
作用
比如你需要上传一个文件,先把这个文件通过hash算法计算出一个 hash 值,然后去和已有的文件进行碰撞,如果已经上传了一样的文件,则不需要上传文件流,直接成功了。
手写HashMap(简易版)
思路
- 我们先定义个存储K-V的类,在定义这个类的数组,初始长度16(jdk源码的HashMap就是16)
- 将key值通过hash算法计算出 hash 值(小于数组长度),然后这个hash值就是数组的上的位置
- 由于我们定义的hash算法会出现碰撞,当出现碰撞时,这里用链的方式做处理
- 具体代码如下,此处为了方便测试,因此把数组的长度设置成了2
public class MyHashMap<K, V> {
private Entry<K, V>[] entrys;
private static final Integer initLen = 2;
public Integer size = 0;
class Entry<K, V> {
public K k;
public V v;
public Entry<K, V> next;
public Entry(K k, V v, Entry<K, V> next) {
this.k = k;
this.v = v;
this.next = next;
}
public K getKey() {
return k;
}
public V getValue() {
return v;
}
}
public void put(K k, V v) {
if(entrys==null){
entrys = new Entry[initLen];
}
Integer index = hash(k) % entrys.length;
//处理相同的k所对应的v
for(Entry<K,V> entry = entrys[index];entry!=null; entry = entry.next){
if(entry.getKey().equals(k)){
entry.v = v;
return;
}
}
addEntry(k, v, index);
}
public V get(K k) {
Integer index = hash(k) % entrys.length;
for(Entry<K,V> entry = entrys[index];entry!=null; entry = entry.next){
if(entry.getKey().equals(k)){
return entry.getValue();
}
}
return null;
}
private void addEntry(K k, V v, Integer index) {
entrys[index] = new Entry<K, V>(k, v, entrys[index]);
size++;
}
private Integer hash(K k) {
return k.hashCode();
}
public static void main(String[] args) {
MyHashMap<String, String> m = new MyHashMap<String, String>();
m.put("a","1");
m.put("b","2");
m.put("c","3");
m.put("b","4");
System.out.println(m.get("a"));
System.out.println(m.get("b"));
System.out.println(m.get("c"));
}
}