哈希表基础
哈希表是一个数组形式的数据结构,其中每个元素被称为桶(bucket)。每个桶可以包含多个键值对。为了确定一个键值对应该存储在哪个桶中,需要使用哈希函数(hash function)来计算键的哈希码(hash code)。
工作流程
-
计算哈希码: 当你向
HashMap
添加一个键值对时,首先会计算键的哈希码。这是通过调用键对象的hashCode()
方法完成的。 -
确定索引位置: 计算得到的哈希码经过一定的算法转换成桶的索引位置(index)。索引计算公式通常是
index = hash % table.length
,其中table.length
是桶数组的长度。 -
解决碰撞: 如果多个键值对的哈希码计算出来的索引相同,则会发生碰撞。
HashMap
使用链表或者红黑树来解决碰撞问题:- 在 Java 8 及之前版本中,如果多个键值对的哈希码相同,它们会被链接到同一个桶中的链表上。
- 从 Java 8 开始,当链表中的节点数量达到一定阈值时(默认为 8),链表会被转化为红黑树,以提高查找效率。当树中的节点数量低于另一个阈值(默认为 6)时,红黑树又会退化回链表。
-
存储键值对: 键值对被存储在指定桶的链表或红黑树中。每个键值对以
Node
形式存储,其中包含键、值、哈希码和指向下一个节点的引用。 -
查找键值对: 当你通过键查询值时,
HashMap
会再次计算键的哈希码,并使用相同的索引计算公式找到桶的位置。然后遍历该桶中的链表或红黑树,使用equals()
方法来判断键是否匹配。
扩容机制
随着键值对的增多,哈希表可能会变得拥挤,这会导致更多的碰撞和降低性能。为了保持性能,HashMap
在桶数组达到一定的负载因子(load factor)时会自动扩容。负载因子是桶数组的大小和已存储的键值对数量之间的比率,默认为 0.75。这意味着当已存储的键值对数量超过数组长度的 75% 时,HashMap
将会扩容。
扩容过程中,旧的桶数组会被废弃,创建一个新的更大的桶数组,并将所有键值对重新哈希到新的数组中。这个过程称为“rehashing”。
小练习:
1.将省份和城市的名称保存在集合中,当用户选择省份以后,二级联动,显示对应省份的地级市供用户选择。
效果演示:
class CityMap{
public static Map model = new HashMap();
static {
model.put("北京", new String[] {"北京"});
model.put("上海", new String[] {"上海"});
model.put("天津", new String[] {"天津"});
model.put("重庆", new String[] {"重庆"});
model.put("黑龙江", new String[] {"哈尔滨","齐齐哈尔","牡丹江","大庆","伊春","双鸭山","绥化"});
model.put("吉林", new String[] {"长春","延边","吉林","白山","白城","四平","松原"});
model.put("河北", new String[] {"石家庄","张家口","邯郸","邢台","唐山","保定","秦皇岛"});
}
}
public class ProvinceTest {
public static void main(String[] args) {
Set keySet = CityMap.model.keySet();
for(Object s : keySet) {
System.out.print(s + "\t");
}
System.out.println();
System.out.println("请选择你所在的省份:");
Scanner scan = new Scanner(System.in);
String province = scan.next();
String[] citys = (String[])CityMap.model.get(province);
for(String city : citys) {
System.out.print(city + "\t");
}
System.out.println();
System.out.println("请选择你所在的城市:");
String city = scan.next();
System.out.println("信息登记完毕");
}
}
2.统计字符串中每个字符出现的次数
String str = "aaaabbbcccccccccc";
提示:
char[] arr = str.toCharArray(); //将字符串转换成字符数组
HashMap hm = new HashMap(); //创建双列集合存储键和值,键放字符,值放次数
public class WordCountTest {
public static void main(String[] args) {
String str = "aaaabbbcccccccccc";
char[] arr = str.toCharArray(); // 将字符串转换成字符数组
HashMap map = new HashMap(); // 创建双列集合存储键和值
for (char c : arr) { // 遍历字符数组
if (!map.containsKey(c)) { // 如果不包含这个键
map.put(c, 1); // 就将键和值为1添加
} else { // 如果包含这个键
map.put(c, (int)map.get(c) + 1); // 就将键和值再加1添加进来
}
}
for (Object key : map.keySet()) { // 遍历双列集合
System.out.println(key + "=" + map.get(key));
}
}
}