在通过上篇博客了解了 Map 的维持内部的 键-值 对的基本方式之后,----不了解的看这篇(Java集合的基本概括), 我们便会思考, 在 HashMap 内部是如何组织和排列这些封装了 键-值对的 Map.Entry 实体呢? 如何组织才能达到更高的效率呢?
在了解 HashMap 的散列机制之前, 我们不妨来假设一下 HashMap 内部的数据结构的排列方式.
数组也许是我们最容易想到的一种数据结构, 我们知道, 存储一组数据最快的数据结构便是数组,这也是数组仅存的优点, 同样数组的缺陷也很明显, 比如 需要大量的连续的内存空间, 对元素的插入操作代价高昂,不能随意的扩大容量....等等.
因此,如果在 HashMap 的内部通过数组来维持 键-值 对, 在我们增加元素的时候似乎开销不大, 但是当我们用一个 key 去获取一个 value的时候, 想想我们该怎么去实现, 最常见的方式便是对 key 进行线性的 equals 查询, 从头查到尾, 这样的查询速度如此低下, 效率可想而知, 还有一个缺陷便就是数组与生俱来的, 需要太大的连续的内存空间, 所以通过数组来维持 HashMap 内部的数据缺陷太大,不值得.
在上面说过数组的那么多缺陷之后, 可能很多同学便会说到用链表, 没错链表似乎是解决了数组一个最致命的缺陷(需要大量的连续的内存空间), 但是链表仍然无法解决当我们用一个 key 去查询一个 value 的时候的低下的效率, 原因仍然是从头到尾的线性遍历.....
在上面我们说到用数组或者链表来维护内部的 键-值 对似乎效率都很低下,有没有一种能够结合数组和链表两者各自的优点然后又有着高效的查询速度的数据结构呢?
这便是 HashMap 内部维护数据的方式 --- 数组链表+散列机制
散列的价值便在于速度, 散列使得查询得以快速进行, 那么什么是数组链表,什么又是散列机制呢?
在了解了数组链表和散列机制后,我们再来想一想 HashMap 内部的 键-值 对是如何高效的维持的.
通过上面的解释, 采用 数组链表和散列机制 来实现的 HashMap为什么效率这么高的原因理解了, 下面来实现一个简单的 HashMap.
class MyHashMap extends AbstractMap {
private static final int SIZE = 16;
LinkedList>[] buckets = new LinkedList[SIZE];
@Override
public v put(k key, v value) {
int index = key.hashCode() % SIZE;
if (buckets[index] == null) {
buckets[index] = new LinkedList<>();
}
boolean found = false;
Map.Entry pair = new HashMap.SimpleEntry<>(key, value);
v oldValue = null;
// Begin to found whether already have this key and value.
for (Entry kvEntry : buckets[index]) {
// If already have this key, replace value.
if (kvEntry.getKey().equals(key)) {
oldValue = kvEntry.getValue();
found = true;
buckets[index].set(buckets[index].indexOf(kvEntry), pair);
break;
}
}
// If not found, add the new key-value to the linked end.
if (!found) {
buckets[index].add(pair);
}
return oldValue;
}
@Override
public v get(Object key) {
// Use hash code to get index.
int index = key.hashCode() % SIZE;
if (buckets[index] == null) {
return null;
}
for (Entry kvEntry : buckets[index]) {
// If have this key, return value.
if (kvEntry.getKey().equals(key)) {
return kvEntry.getValue();
}
}
return null;
}
@Override
public Set> entrySet() {
HashSet> entryHashSet = new HashSet<>();
for (LinkedList> bucket : buckets) {
if (bucket != null) {
entryHashSet.addAll(bucket);
}
}
return entryHashSet;
}
}
未完待续......对HashMap源码的解析.....