Hash 一般也可叫做散列,你可以把 Hash 简单的理解为将一个对象通过 hashCode() 方法映射为一个 int 类型的值,其中 hashCode() 是定义在 Object 中的,而 java 中所有的类都继承自 Object。
所以所有的类都有默认的 hashCode() 方法,你可以根据自己的需要去进行重写。

以你说的 HashMap 为例:HashMap 在 JDK 默认的实现是 数组+链表,也就是一个数组,数组中的每一个元素都是一个链表,假设数组为 array,链表为 list。

HashMap 是对 key 做出了散列处理,也就是求出了 key 所对应的一个 int 值。
比如说你声明了一个HashMap<String, Object> 类型,如果你调用一次 map.put(“key”, object)。
那么就先调用 “key”.hashCode() 得到一个 int,这个 int 就是 “key” 的哈希值,用得到的这个 int 类型的值余上 array 的长度,就得到了这个 object 在 array 中的位置。

但是,在实际的使用中,可能会出现一种情况就是两个不同的对象的哈希值可能是相同的,假设 “key1” 和 “key2” 得到了相同的哈希值,那么这个时候我们调用 map.put(“key1”, object1) 和 map(“key2”, object2) 他们通过上面的步骤算出来的哈希值是相同的,也就是他们在 array 中的位置是相同的,这个就叫做 散列冲突。

所以这就是为什么 JDK 使用 数组+链表 的实现形式了。我们通过上面的步骤得到一个对象在 array 中的位置,如果他们的哈希值相同,但是他们不是同一个 String,那么我们就将后添加的这个 object 添加到 list 中;如果他们是同一个 String,那么我们就用新的 object 覆盖它在 list 中的上一个 object。

例如,对于 “key1” 和 “key2” 通过散列得到的在 array 中的位置是相同的,那么我们判断 “key1”.equals(“key2”),如果返回 true,我们使用 object2 覆盖 object1,如果 返回 false,我们将 object2 添加到 list 中。

所以上面调用的结果应该如下,其中 2 是 “key1” 和 “key2” 通过散列的出来的在 array 中的位置:

{
    list0:null,
    list1:null,
    list2:[object1, object2]
}

JDK 使用的这种叫做分离链接法,还有一种叫做平方探测发法。有兴趣的话可以去看看《数据结构与算法分析》这本书,好像在第 5 章。