Hashmap源码解析 keyset,entryset
HashMap 继承于AbstractMap,
同时实现了Map接口:属于Map的子类。
Cloneable接口:可以被复制。
Serializable:可以被序列化。
HashMap是一个键值对的集合,通常通过
HashMap.put(K,V)添加单个元素。
本文主要讨论Hashmap中的实现细节,put函数,keyset和entryset函数。
/**
* An empty table instance to share when the table is not inflated.
*/
static final HashMapEntry<?,?>[] EMPTY_TABLE = {};
/**
* The table, resized as necessary. Length MUST Always be a power of two.
*/
transient HashMapEntry<K,V>[] table = (HashMapEntry<K,V>[]) EMPTY_TABLE;
首先构造了一个为空的名为table 的HashMapEntry的数组,这里将名为EMPTY_TABLE的空数组的对象赋给了table (不知道这里这样赋值的意义)
既然这里创建了HashMapEntry,我们来看一下这个类是什么
static class HashMapEntry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
HashMapEntry<K,V> next;
int hash;
/**
* Creates new entry.
*/
HashMapEntry(int h, K k, V v, HashMapEntry<K,V> 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 Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
}
public final String toString() {
return getKey() + "=" + getValue();
}
/**
* This method is invoked whenever the value in an entry is
* overwritten by an invocation of put(k,v) for a key k that's already
* in the HashMap.
*/
void recordAccess(HashMap<K,V> m) {
}
/**
* This method is invoked whenever the entry is
* removed from the table.
*/
void recordRemoval(HashMap<K,V> m) {
}
}
实质上是一个Hashmap中的内部类,这里的k和v实际就是我们传入的键值对的值,值得注意的是其中的变量HashMapEntry
put方法
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold); //扩充数组
}
if (key == null) //对key进行判断
return putForNullKey(value);
int hash = sun.misc.Hashing.singleWordWangJenkinsHash(key); //得到hash值
int i = indexFor(hash, table.length); //通过hash值得到具体的位置
for (HashMapEntry<K,V> e = table[i]; e != null; e = e.next) { //判断该HashMapEntry是否为空
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;
}
同样的,进入put方法时候需要对数组的大小进行判断,如果为空的化,就会动态申请数组,这里和LinkedList一致,因为是利用数组实现的。
接着对key进行判空,如果为空的化就执行putForNullKey方法。
private V putForNullKey(V value) {
for (HashMapEntry<K,V> e = table[0]; e != null; e = e.next) { //默认将key为null填入第一个,所以这里进行判断,将key为空的value替换掉
if (e.key == null) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(0, null, value, 0); //如果第一次添加key为null的HashMapEntry,或者如果判断的前面的HashMapEntry都不为空,同时其key也不为空时候会执行添加操作,同样默认将其添加到第一个位置
return null;
}
这里首先从第一个HashMapEntry开始判断,如果key同样是空的话,就会直接替换其value,如果找到了为空的话,直接将其添加到0的位置。
接下来的解析就像上面代码那样。
keyset
keyset是map中一个用于装化为set的函数,同时也是map中的原始函数,这里同样贴出keyset的代码
public Set<K> keySet() {
Set<K> ks = keySet;
return (ks != null ? ks : (keySet = new KeySet()));
}
可以看到只有短短的一行,将ks的引用指向keySet,同时判断keySet是否为空,如果为空的话,就对其进行new KeySet()操作。接着我们看new KeySet()类中的操作
private final class KeySet extends AbstractSet<K> {
public Iterator<K> iterator() { //实现了iterator实例
return newKeyIterator();
}
public int size() {
return size;
}
public boolean contains(Object o) {
return containsKey(o);
}
public boolean remove(Object o) {
return HashMap.this.removeEntryForKey(o) != null;
}
public void clear() {
HashMap.this.clear();
}
public final Spliterator<K> spliterator() {
return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer<? super K> action) { //java8中Lambda表达式部分
HashMapEntry<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (HashMapEntry<K,V> e = tab[i]; e != null; e = e.next) {
action.accept(e.key);
// Android-modified - this was outside of the loop, inconsistent with other
// collections
if (modCount != mc) {
throw new ConcurrentModificationException();
}
}
}
}
}
}
这里我们着重看下面的代码,层层调用最后到了HashIterator中
public Iterator<K> iterator() { //实现了iterator实例
return newKeyIterator(); //实际返回了一个KeyIterator
}
这里实现了实现了iterator实例,也就是其可以通过foreach对其进行遍历,我们继续查看
Iterator<K> newKeyIterator() {
return new KeyIterator(); //返回一个KeyIterator实例
}
private final class KeyIterator extends HashIterator<K> { //由于KeyIterator 继承于HashIterator。所以可以直接调用其方法。
public K next() { //重写了next方法
return nextEntry().getKey(); //next方法直接定位到了nextEntry方法中
}
private abstract class HashIterator<E> implements Iterator<E> {
HashMapEntry<K,V> next; // next entry to return //下一个需要返回的HashMapEntry
int expectedModCount; // For fast-fail
int index; // current slot
HashMapEntry<K,V> current; // current entry //当前的HashMapEntry
HashIterator() {
expectedModCount = modCount;
if (size > 0) { // advance to first entry
HashMapEntry[] t = table;
while (index < t.length && (next = t[index++]) == null) //构造方法中通过不断的遍历,直到找到那个不为空的HashMapEntry为止
;
}
}
public final boolean hasNext() {
return next != null; //如果为空,则说明后面不在存在。
}
final Entry<K,V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
HashMapEntry<K,V> e = next;
if (e == null) //如果为空,则直接抛出异常
throw new NoSuchElementException();
if ((next = e.next) == null) { //如果其后面链表中的HashMapEntry同样为空,说明值已经取完了,可以直接进行查找下一个HashMapEntry
HashMapEntry[] 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;
}
}
这里我们就理清楚了,实质上其iterator是返回的HashIterator,同时在HashIterator中实现了一些具体的操作,这样就完成一个完整的iterator。
既然iterator都有了就可以进行遍历了,那这个代码最后的forEach又是什么鬼东西,传入的参数还是
Consumer< super K> action,分析下面的代码,直到他也是一个类似于遍历的函数,通过不断的遍历table中的值,同时通过action.accept将其取出。
查阅资料知道了这是java8中的Lambda表达式部分,我们通过这里重写了Lambda表达式的逻辑。这里同样是通过遍历,实现了依次取出值的目的,同时通过action.accept(e.key),得出需要取出的值。
public final void forEach(Consumer<? super K> action) { //java8中Lambda表达式部分
HashMapEntry<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (HashMapEntry<K,V> e = tab[i]; e != null; e = e.next) { //循环找出不为空的HashMapEntry
action.accept(e.key);
// Android-modified - this was outside of the loop, inconsistent with other
// collections
if (modCount != mc) {
throw new ConcurrentModificationException();
}
}
}
}
}
这里为了验证其重写操作,我们进行依次代码验证,这里实现了一个简单的类En ,里面一个int数组,同时重写了forEach,实现从4,3,2,1的输出和Iterator实例,实现从1,2,3,4的输出。同时编 写代码测试正确性。
可以看到程序输出4,3,2,1 和1,2,3,4。
En en=new En();
en.forEach(cc->Log.e(TAG,cc)); //通过Lambda表达式输出
for (String s:en){ //通过foreach进行输出
Log.e(TAG,s);
}
public class En extends AbstractSet<String>{
int a[]=new int[4];
int flag=0;
public En(){
a[0]=1;
a[1]=2;
a[2]=3;
a[3]=4;
}
@RequiresApi(api = Build.VERSION_CODES.N)
@Override
public void forEach(Consumer<? super String> action) {
for (int i=3;i>=0;i--){
action.accept(String.valueOf(a[i]));
}
}
@Override
public Iterator<String> iterator() {
return new Iterator<String>() {
@Override
public boolean hasNext() {
if (flag<4){
return true;
}
return false;
}
@Override
public String next() {
return String.valueOf(a[flag++]);
}
};
}
@Override
public int size() {
return 4;
}
}
同样我们考察entryset,发现其和 keyset本质是一样的,只是在代码中返回的变成了Set<Map.Entry<K,V>>
,同时遍历时候将key和value的值同时存入其中,有兴趣可以下去讨论下。