哈希表,也叫散列表,根据键(Key)而直接访问在内存存储位置的数据结构,通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度
key-value 对,key 不重复
映射函数称做“哈希函数”,存放记录的数组称做“哈希表”或者“散列表”
映射的位置,即为哈希表的下标 Index
哈希表中不可能存储引用数据类型的对象数据,只是存储引用,即存储对象数据的地址
散列表设计的关键
关键在于 hash 函数,设计好,冲突较少,能够让数值分散均匀,否则,较为集中,导致单链表太多,效率降低,时间复杂度由 O(1) 变为 O(n)
简单哈希函数
简单的哈希函数,把每一个字符,它的 ASCII 码加在一起,然后再 mod(取模)上一个数,最后的结果是多少就是多少
散列表冲突的解决方法:拉链式解决冲突法
Java 中的 Map 定义为一个接口,接口就是定义一种能力,没有具体代码实现,具体实现交由子类完成
public interface Map
HashMap、TreeMap、HashTable、ConcurrentHashMap、LinkedHashMap、Properties
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
......
}
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
......
}
public class TreeMap<K,V>
extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, java.io.Serializable{
private final Comparator<? super K> comparator;
private transient Entry<K,V> root;
......
}
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable {
private transient Entry<?,?>[] table;
public synchronized V get(Object key) {
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
return (V)e.value;
}
}
return null;
}
......
}
public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
implements ConcurrentMap<K,V>, Serializable {
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
volatile V val;
volatile Node<K,V> next;
......
}
public V put(K key, V value) {
return putVal(key, value, false);
}
/** Implementation for put and putIfAbsent */
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh; K fk; V fv;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value)))
break; // no lock when adding to empty bin
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else if (onlyIfAbsent // check first node without acquiring lock
&& fh == hash
&& ((fk = f.key) == key || (fk != null && key.equals(fk)))
&& (fv = f.val) != null)
return fv;
else {
V oldVal = null;
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f;; ++binCount) {
K ek;
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val;
if (!onlyIfAbsent)
e.val = value;
break;
}
Node<K,V> pred = e;
if ((e = e.next) == null) {
pred.next = new Node<K,V>(hash, key, value);
break;
}
}
}
else if (f instanceof TreeBin) {
Node<K,V> p;
binCount = 2;
if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
value)) != null) {
oldVal = p.val;
if (!onlyIfAbsent)
p.val = value;
}
}
else if (f instanceof ReservationNode)
throw new IllegalStateException("Recursive update");
}
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}
......
}
public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>
{
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
transient LinkedHashMap.Entry<K,V> head;
transient LinkedHashMap.Entry<K,V> tail;
......
}
public class Properties extends Hashtable<Object,Object> {
private transient volatile ConcurrentHashMap<Object, Object> map;
......
}
map_x = {
'jack': 100,
'张三': 80,
'Selina': 90,
......
}
极客时间《数据结构与算法之美》21 | 哈希算法(上):如何防止数据库中的用户信息被脱库?
极客时间《数据结构与算法之美》22 | 哈希算法(下):哈希算法在分布式系统中有哪些应用?