数据结构——哈希表
哈希表:通过关键码来映射到值的一个数据结构
哈希函数:键和值的映射关系
两种方法:
(1)直接寻址法
f(x) = kx + b——(k、b都是常数)
(2)除留余数法
f(x) = x % k ——(k < m,m是存储长度)
哈希冲突:f(m)、f(n)
m != n —— >f(m) == f(n)
注:哈希冲突是不可避免的,但是选择适当的哈希函数可以减小发生冲突的概率
解决哈希冲突的两种方法:
(1)链地址法;
(2)探测法——(线性探测、随机探测)
//遍历
//1、键值对遍历
Iterator<Map.Entry<String,String>> iterator = hashMap.entrySet().iterator();
while(iterator.hasNext()){
Map.Entry<String,String> next = iterator.next();
String key = next.getKey();
String val = next.getValue();
System.out.print("[" + key + " " + val + "] ,");
}
System.out.println();
//2、键遍历
Iterator<String> iterator1 = hashMap.keySet().iterator();
while (iterator1.hasNext()){
String key = iterator1.next();
System.out.print(key + " ,");
}
System.out.println();
//3、值遍历
Iterator<String> iterator2 = hashMap.values().iterator();
while(iterator2.hasNext()){
String val = iterator2.next();
System.out.print(val + " ,");
}
System.out.println();
源码分析
(1)继承关系
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
(2)默认值
默认大小(16),默认加载因子(0.75)
容量是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。
当哈希表中的条目数超出了加载因子与当前容量的乘积时,就要进行扩容、rehash操作(重建内部数据结构),扩容后的哈希表将具有2倍的原容量。
(*)0.75怎么来的?
加载因子过高,虽然减少了空间开销提高了空间利用率,但是同时增加了查询时间成本。
加载因子过低,可以减少查询时间成本,但是空间使用率较低,同时还增加了rehash操作的次数。
在源码注释中也只说0.75是一种折中选择。
在理想情况下使用随机哈希码,节点出现的频率在hash桶中遵循泊松分布,同时给出了桶中元素的个数个概率的对照表。从表中可以看出当桶中元素到达8个时,概率已经变得很小。也就是说用0.75作为负载因子,每个碰撞位置的链表长度超过8个时几乎不可能的。HashMap负载因子为0.75是空间和时间成本的一种折中。
(*)hash容器指定初始容量尽量为2的幂次方。
(*)当哈希冲突足够多时链表会不会很长?
???
(3)基本属性
/**
* The default initial capacity - MUST be a power of two.
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
/**
* The maximum capacity, used if a higher value is implicitly specified
* by either of the constructors with arguments.
* MUST be a power of two <= 1<<30.
*/
static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* The load factor used when none specified in constructor.
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* An empty table instance to share when the table is not inflated.
*/
static final Entry<?,?>[] EMPTY_TABLE = {};
/**
* The table, resized as necessary. Length MUST Always be a power of two.
*/
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
/**
* The number of key-value mappings contained in this map.
*/
transient int size;
/**
* The next size value at which to resize (capacity * load factor).
* @serial
*/
// If table == EMPTY_TABLE then this is the initial capacity at which the
// table will be created when inflated.
int threshold;
/**
* The load factor for the hash table.
*
* @serial
*/
final float loadFactor;
(4)构造函数(4个)
(5)增长方式
(6)CURD(增删改查)
扩容:
???