作者:手浪用户2602890531 | 来源:互联网 | 2023-01-02 12:11
众所周知,Map是用来存储键值对的数据的,而且他的好处就是根据键值能够快速定位,技能保持这ArrayList的优势,又能够保持LinkedList的容易删除和增加的优势,那么我们来分析分析他的实
众所周知,Map是用来存储键值对的数据的,而且他的好处就是根据键值能够快速定位,技能保持这ArrayList的优势,又能够保持LinkedList的容易删除和增加的优势,那么我们来分析分析他的实现机理,老规矩,先给出类图:
首先先来分析一下HashMap
static final int DEFAULT_INITIAL_CAPACITY = 16;
最大容量2的15次方+1;
static final int MAXIMUM_CAPACITY = 1 <<30;
默认加载因子:
static final float DEFAULT_LOAD_FACTOR = 0.75f;
内部实现的机制是用具有键值对格式的单个的entry数组实现:
- transient Entry[] table;
- static class Entry implements Map.Entry {
- final K key;
- V value;
- Entry next;
- final int hash;
-
- Entry(int h, K k, V v, Entry n) {
- value = v;
- next = n;
- key = k;
- hash = h;
- }
- public final K getKey() {
- return key;
- }
- public final V getValue() {
- return value;
- }
- public final V setValue(V newValue) {
- V oldValue = value;
- value = newValue;
- return oldValue;
- }
- public final boolean equals(Object o) {
- if (!(o instanceof Map.Entry))
- return false;
- Map.Entry e = (Map.Entry)o;
- Object k1 = getKey();
- Object k2 = e.getKey();
- if (k1 == k2 || (k1 != null && k1.equals(k2))) {
- Object v1 = getValue();
- Object v2 = e.getValue();
- if (v1 == v2 || (v1 != null && v1.equals(v2)))
- return true;
- }
- return false;
- }
- public final int hashCode() {
- return (key==null ? 0 : key.hashCode()) ^
- (value==null ? 0 : value.hashCode());
- }
- public final String toString() {
- return getKey() + "=" + getValue();
- }
- void recordAccess(HashMap m) {
- }
- void recordRemoval(HashMap m) {
- }
- }
当前的数组大小:
transient int size;
构造函数初始化:
设置初始化的容积和加载因子
- public HashMap(int initialCapacity, float loadFactor) {
-
- if (initialCapacity < 0)
- throw new IllegalArgumentException("Illegal initial capacity: " +
- initialCapacity);
-
- if (initialCapacity > MAXIMUM_CAPACITY)
- initialCapacity = MAXIMUM_CAPACITY;
-
- if (loadFactor <= 0 || Float.isNaN(loadFactor))
- throw new IllegalArgumentException("Illegal load factor: " +
- loadFactor);
-
- int capacity = 1;
- while (capacity < initialCapacity)
- capacity <<= 1;
- this.loadFactor = loadFactor;
- threshold = (int)(capacity * loadFactor);
-
- table = new Entry[capacity];
-
- init();
- }
这是一个hashcode的转换算法;他能够把对象的hashcode转换成为小于length-1的整数作为数组的下标;那么势必会出现重合
- static int hash(int h) {
- h ^= (h >>> 20) ^ (h >>> 12);
- return h ^ (h >>> 7) ^ (h >>> 4);
- }
- static int indexFor(int h, int length) {
- return h & (length-1);
- }
我们先看增加方法:
- public V put(K key, V value) {
-
- if (key == null)
- return putForNullKey(value);
-
-
- int hash = hash(key.hashCode());
-
- int i = indexFor(hash, table.length);
-
-
- for (Entry e = table[i]; e != null; e = e.next) {
- Object k;
-
- if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
- V oldValue = e.value;
- e.value = value;
- e.recordAccess(this);
- return oldValue;
- }
- }
-
- modCount++;
-
- addEntry(hash, key, value, i);
- return null;
- }
- void addEntry(int hash, K key, V value, int bucketIndex) {
- Entry e = table[bucketIndex];
-
- table[bucketIndex] = new Entry(hash, key, value, e);
-
- if (size++ >= threshold)
- resize(2 * table.length);
- }
如果key是null的话:
那么他可以不用通过hashcode定位数组中的队列下标-_-||事实上他也没有hashcode;规定在0位存放这个链表的头;可见在HashMap中是可以
存放null的key的;但是正因为其没有hashcode那么就只能存放一个元素,而不是像其他一样能存放多个;但是另外我们可见,你可以考虑把使
用的最多的值的键设置成为null,因为他是找到的最快的;
- private V putForNullKey(V value) {
- for (Entry e = table[0]; e != null; e = e.next) {
- if (e.key == null) {
- V oldValue = e.value;
- e.value = value;
- e.recordAccess(this);
- return oldValue;
- }
- }
- modCount++;
- addEntry(0, null, value, 0);
- return null;
- }
上面说了存放了,下面我们再来看看如何取出来把:
- final Entry getEntry(Object key) {
-
-
- int hash = (key == null) ? 0 : hash(key.hashCode());
- for (Entry e = table[indexFor(hash, table.length)];
- e != null;
- e = e.next) {
- Object k;
-
- if (e.hash == hash &&
- ((k = e.key) == key || (key != null && key.equals(k))))
- return e;
- }
-
- return null;
我们再来看看其容量是如何增长的:
- public void putAll(Map extends K, ? extends V> m) {
-
- int numKeysToBeAdded = m.size();
- if (numKeysToBeAdded == 0)
- return;
-
- if (numKeysToBeAdded > threshold) {
-
- int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
- if (targetCapacity > MAXIMUM_CAPACITY)
- targetCapacity = MAXIMUM_CAPACITY;
- int newCapacity = table.length;
-
- while (newCapacity < targetCapacity)
- newCapacity <<= 1;
- if (newCapacity > table.length)
- resize(newCapacity);
- }
-
-
- for (Iterator extends Map.Entry extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) {
- Map.Entry extends K, ? extends V> e = i.next();
- put(e.getKey(), e.getValue());
- }
- }
用新的容积开辟了一块足够大的存储空间,
- void resize(int newCapacity) {
- Entry[] oldTable = table;
- int oldCapacity = oldTable.length;
- if (oldCapacity == MAXIMUM_CAPACITY) {
- threshold = Integer.MAX_VALUE;
- return;
- }
- Entry[] newTable = new Entry[newCapacity];
- transfer(newTable);
- table = newTable;
- threshold = (int)(newCapacity * loadFactor);
- }
- 并且把家具从以前的“小房子”帮到了“大房子”
- void transfer(Entry[] newTable) {
- Entry[] src = table;
- int newCapacity = newTable.length;
- for (int j = 0; j < src.length; j++) {
-
- Entry e = src[j];
- if (e != null) {
- src[j] = null;
- do {
- Entry next = e.next;
- int i = indexFor(e.hash, newCapacity);
- e.next = newTable[i];
- newTable[i] = e;
- e = next;
- } while (e != null);
- }
- }
- }
新增,查找,都看过了,我们来看一下删除:
- public V remove(Object key) {
- Entry e = removeEntryForKey(key);
- return (e == null ? null : e.value);
- }
-
- final Entry removeEntryForKey(Object key) {
-
- int hash = (key == null) ? 0 : hash(key.hashCode());
- int i = indexFor(hash, table.length);
- Entry prev = table[i];
- Entry e = prev;
- while (e != null) {
- Entry next = e.next;
- Object k;
- if (e.hash == hash &&
- ((k = e.key) == key || (key != null && key.equals(k)))) {
- modCount++;
-
- size--;
- if (prev == e)
- table[i] = next;
- else
- prev.next = next;
- e.recordRemoval(this);
- return e;
- }
-
- 一个节点的指
- prev = e;
- e = next;
- }
- return e;
- }
现在我们再看一下如何单独取到他的键值集合或者值集合:
- values实现了一个内部私有类;
- public Collection values() {
- Collection vs = values;
- return (vs != null ? vs : (values = new Values()));
- }
- private final class Values extends AbstractCollection {
-
-
- public Iterator iterator() {
- return newValueIterator();
- }
- public int size() {
- return size;
- }
- public boolean contains(Object o) {
- return containsValue(o);
- }
- public void clear() {
- HashMap.this.clear();
- }
- }
- 下面是该iteartor的获取:其中主要的是看看我们获取的value的collection是如何排序的;
- private abstract class HashIterator implements Iterator {
- Entry next;
- int expectedModCount;
- int index;
- Entry current;
- HashIterator() {
- expectedModCount = modCount;
-
- if (size > 0) {
- Entry[] t = table;
- 然后把next和index都调至该table数组的链表头中不为空的地方;
- while (index < t.length && (next = t[index++]) == null)
- ;
- }
- }
- public final boolean hasNext() {
- return next != null;
- }
- final Entry nextEntry() {
- if (modCount != expectedModCount)
- throw new ConcurrentModificationException();
- Entry e = next;
- if (e == null)
- throw new NoSuchElementException();
-
- if ((next = e.next) == null) {
- Entry[] t = table;
-
- while (index < t.length && (next = t[index++]) == null)
- ;
- }
- current = e;
- return e;
- }
- public void remove() {
- if (current == null)
- throw new IllegalStateException();
- if (modCount != expectedModCount)
- throw new ConcurrentModificationException();
- Object k = current.key;
- current = null;
- HashMap.this.removeEntryForKey(k);
- expectedModCount = modCount;
- }
- }
- ValueIterator :
- private final class ValueIterator extends HashIterator {
- public V next() {
- return nextEntry().value;
- }
- }
这是我们会问加入我在这个value的collection中增加元素会出现什么样的情况呢,次序会不会打乱,对不起,AbstractCollection不存在add
的,iteartor也是不允许增加元素的;
对于来说keySet来说是同理的,不过因为key是不存在一样的,所以,我们不会返回collection而返回set,避免了重复key的出现;
好,我们今天先把HashMap分析完成;