android源码分析-------LinkedHashMap,HashMap

    xiaoxiao2025-05-29  6

    这篇文章是LurCache源码分析的后续,如果想看LurCache源码分析,请转上一篇:《 避免OOM系列之(二)-------LruCache使用以及源码详细解析》。

    我们先来看下继承关系:

    们看到LinkedHashMap 继承HashMap  ,HashMap继承AbstractMap,如果你查看AbstractMap源码会发现,AbstractMap实现了Map,所有我们就从Map开始分析:

    Map

    //map接口功能类 public interface Map { //单个元素,接口功能类 public static interface Entry { //是否相等 public boolean equals(Object object); //获取key public K getKey(); //获取value public V getValue(); //获取哈希值 public int hashCode(); //设置值 public V setValue(V object); }; //清空 public void clear(); //是否包含key public boolean containsKey(Object key); //是否包含这个值 public boolean containsValue(Object value); //获取map中的元素结合(最核心的方法) public Set > entrySet(); //是否相等 public boolean equals(Object object); //获取元素 public V get(Object key); //获取哈希code public int hashCode(); //判断是否为空 public boolean isEmpty(); //获取key的集合(核心) public Set keySet(); //存放元素 public V put(K key, V value); //存放元素结合 public void putAll(Map map); //移除元素 public V remove(Object key); //当前大小 public int size(); //获取当前值得集合(核心) public Collection values(); } 可以看到Map,其实就是功能接口,只要关注Entry<K,V>,entrySet(),values(),keySet()的实现就可以了,剩下的只是一些常用的方法。

    AbstractMap

    虽然 AbstractMap,实现了Map一部分方法和类,但是在HashMap中都已经被重写了,所有不需要关注。HashMap 是我们分析的核心。

    HashMap(重点):

    public class HashMap extends AbstractMap implements Cloneable, Serializable { //数组最小元素个数 private static final int MINIMUM_CAPACITY = 4; //数组最大元素个数(2的30次方) private static final int MAXIMUM_CAPACITY = 1 << 30; //空数组,初始化元素个数(2) private static final Entry[] EMPTY_TABLE = new HashMapEntry[MINIMUM_CAPACITY >>> 1]; //当元素个数到达(3/4)数量是,扩展 static final float DEFAULT_LOAD_FACTOR = .75F; //核心数组(table) transient HashMapEntry [] table; //key值为NULL的元素 transient HashMapEntry entryForNullKey; //数组元素的个数 transient int size; //元素修改的次数(包括添加修改删除更新) transient int modCount; //这三个字段,只有被用的时候才参数 //key集合 private transient Set keySet; //元素集合 private transient Set > entrySet; //值集合 private transient Collection values; //capacity,必须是2的整数倍, public HashMap(int capacity) { if (capacity < 0) { throw new IllegalArgumentException("Capacity: " + capacity); } if (capacity == 0) { @SuppressWarnings("unchecked") HashMapEntry [] tab = (HashMapEntry []) EMPTY_TABLE; table = tab; threshold = -1; // Forces first put() to replace EMPTY_TABLE return; } if (capacity < MINIMUM_CAPACITY) { capacity = MINIMUM_CAPACITY; } else if (capacity > MAXIMUM_CAPACITY) { capacity = MAXIMUM_CAPACITY; } else { //如果capacity不是2的整数倍,就将他转成比capacity小的,2的整数倍值 capacity = Collections.roundUpToPowerOfTwo(capacity); } //扩大数组元素个数 makeTable(capacity); } 上面是HashMap中主要的字段,和构造函数。从字段中可以看出 HashMap真正存储数据的是table,那我们先来分析table: 首先table是一个数组,那么数据结构就是这样: 那么元素又是什么呢?HashMapEntry,让我们来看下 HashMapEntry源码: //实现了Map.Entry接口 static class HashMapEntry implements Entry { //key final K key; //value V value; //哈希值 final int hash; //指向下一个元素 HashMapEntry next; HashMapEntry(K key, V value, int hash, HashMapEntry next) { this.key = key; this.value = value; this.hash = hash; this.next = next; } public final K getKey() { return key; } public final V getValue() { return value; } public final V setValue(V value) { V oldValue = this.value; this.value = value; return oldValue; } @Override public final boolean equals(Object o) { if (!(o instanceof Entry)) { return false; } Entry e = (Entry ) o; return Objects.equal(e.getKey(), key) && Objects.equal(e.getValue(), value); } @Override public final int hashCode() { return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode()); } @Override public final String toString() { return key + "=" + value; } } 我们看源码发现, 其实就是一个单向链表(元素),那map是怎么来存储元素的呢?

    我们来看下put方法: //添加元素 @Override public V put(K key, V value) { //如果key为空,就将元素添加到entryForNullKey if (key == null) { return putValueForNullKey(value); } //得到key的hashcode int hash = secondaryHash(key); HashMapEntry [] tab = table; //根据hash值计算出数组下标 int index = hash & (tab.length - 1); //遍历元素,如果有这个元素,就直接返回 for (HashMapEntry e = tab[index]; e != null; e = e.next) { if (e.hash == hash && key.equals(e.key)) { preModify(e); V oldValue = e.value; e.value = value; return oldValue; } } // No entry for (non-null) key is present; create one modCount++; //如果元素的个数大于容量的3/4 .就扩展一倍容量,并且得到新的下标好 if (size++ > threshold) { tab = doubleCapacity(); index = hash & (tab.length - 1); } //添加新的元素,如果对应下标有元素,就将新的元素添加到这个元素的后面,形成单向链表 addNewEntry(key, value, hash, index); return null; }

    可以看到,table中的元素其实就是单向链表,那么现在的table数据结构就是:

    我们已经得到了HashMap的数据结构,就是数组和单向链表构成,这个被称作“散列“。 HashMap查找元素时使用的是hash,这个大家可能很不理解,为什么要使用hash,主要的原因是快,不需要遍历来查找元素, 只需要key的hash,和表的长度,据可以得到下标,直接一步定位。 分析keySet,entrySet,values,这几个字段是遍历时使用,下面通过源码来看下值得由来: //获取元素集合 public Set > entrySet() { Set > es = entrySet; return (es != null) ? es : (entrySet = new EntrySet()); } //获取key集合 @Override public Set keySet() { Set ks = keySet; return (ks != null) ? ks : (keySet = new KeySet()); } //获取value集合 @Override public Collection values() { Collection vs = values; return (vs != null) ? vs : (values = new Values()); } 可以看出,迭代数据时,得到的是EntrySet,KeySet,Values三个对象,我们继续看源码: //得到table中的所有元素, private abstract class HashIterator { int nextIndex; HashMapEntry nextEntry = entryForNullKey; HashMapEntry lastEntryReturned; int expectedModCount = modCount; HashIterator() { if (nextEntry == null) { HashMapEntry [] tab = table; HashMapEntry next = null; while (next == null && nextIndex < tab.length) { next = tab[nextIndex++]; } nextEntry = next; } } public boolean hasNext() { return nextEntry != null; } //得到table中的所有元素 HashMapEntry nextEntry() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); if (nextEntry == null) throw new NoSuchElementException(); HashMapEntry entryToReturn = nextEntry; HashMapEntry [] tab = table; HashMapEntry next = entryToReturn.next; while (next == null && nextIndex < tab.length) { next = tab[nextIndex++]; } nextEntry = next; return lastEntryReturned = entryToReturn; } public void remove() { if (lastEntryReturned == null) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); HashMap.this.remove(lastEntryReturned.key); lastEntryReturned = null; expectedModCount = modCount; } } //key迭代器类 private final class KeyIterator extends HashIterator implements Iterator { public K next() { return nextEntry().key; } } //value迭代器类 private final class ValueIterator extends HashIterator implements Iterator { public V next() { return nextEntry().value; } } //元素迭代器类 private final class EntryIterator extends HashIterator implements Iterator > { public Entry next() { return nextEntry(); } } //得到迭代器的方法 Iterator newKeyIterator() { return new KeyIterator(); } Iterator newValueIterator() { return new ValueIterator(); } Iterator > newEntryIterator() { return new EntryIterator(); } //key集合的类 private final class KeySet extends AbstractSet { //迭代器实现 public Iterator iterator() { return newKeyIterator(); } public int size() { return size; } public boolean isEmpty() { return size == 0; } public boolean contains(Object o) { return containsKey(o); } public boolean remove(Object o) { int oldSize = size; HashMap.this.remove(o); return size != oldSize; } public void clear() { HashMap.this.clear(); } } //value集合的类 private final class Values extends AbstractCollection { //迭代器实现 public Iterator iterator() { return newValueIterator(); } public int size() { return size; } public boolean isEmpty() { return size == 0; } public boolean contains(Object o) { return containsValue(o); } public void clear() { HashMap.this.clear(); } } //元素集合的类 private final class EntrySet extends AbstractSet > { //迭代器实现 public Iterator > iterator() { return newEntryIterator(); } public boolean contains(Object o) { if (!(o instanceof Entry)) return false; Entry e = (Entry ) o; return containsMapping(e.getKey(), e.getValue()); } public boolean remove(Object o) { if (!(o instanceof Entry)) return false; Entry e = (Entry )o; return removeMapping(e.getKey(), e.getValue()); } public int size() { return size; } public boolean isEmpty() { return size == 0; } public void clear() { HashMap.this.clear(); } } 我们看到,这三个类只是将我们的table中元素封装到set,Collection中,来实现对hashmap元素的迭代。还是一些 获取数据,删除数据,清空,拷贝等常用方法,不在介绍,如果明白上面图中的数据结构,那么代码就很简单了。 接下来我们看下,lrucache中使用的LinkedHashMap,看看它是怎么排序的?怎么快速找到元素的?

    LinkedHashMap:

    //头节点(双向链表) transient LinkedEntry header; //是否按照最近访问排序 private final boolean accessOrder; public LinkedHashMap( int initialCapacity, float loadFactor, boolean accessOrder) { //初始化hashmap super(initialCapacity, loadFactor); init(); this.accessOrder = accessOrder; } //双向链表节点 static class LinkedEntry extends HashMapEntry { //指向next元素 LinkedEntry nxt; //指向前面元素 LinkedEntry prv; LinkedEntry() { super(null, null, 0, null); nxt = prv = this; } LinkedEntry(K key, V value, int hash, HashMapEntry next, LinkedEntry nxt, LinkedEntry prv) { super(key, value, hash, next); this.nxt = nxt; this.prv = prv; } } 可以看到 LinkedHashMap本质上就是双向链表,结构如下: //添加新的元素 @Override void addNewEntry(K key, V value, int hash, int index) { LinkedEntry header = this.header; //获取最老的元素 LinkedEntry eldest = header.nxt; // if (eldest != header && removeEldestEntry(eldest)) { remove(eldest.key); } //添加新元素,将header->pre指向新的元素 LinkedEntry oldTail = header.prv; LinkedEntry newTail = new LinkedEntry ( key, value, hash, table[index], header, oldTail); table[index] = oldTail.nxt = header.prv = newTail; } //添加key 为null的元素,并且添加到链表中 @Override void addNewEntryForNullKey(V value) { LinkedEntry header = this.header; // Remove eldest entry if instructed to do so. LinkedEntry eldest = header.nxt; if (eldest != header && removeEldestEntry(eldest)) { remove(eldest.key); } // Create new entry, link it on to list, and put it into table LinkedEntry oldTail = header.prv; LinkedEntry newTail = new LinkedEntry ( null, value, 0, null, header, oldTail); entryForNullKey = oldTail.nxt = header.prv = newTail; } @Override HashMapEntry constructorNewEntry( K key, V value, int hash, HashMapEntry next) { LinkedEntry header = this.header; LinkedEntry oldTail = header.prv; LinkedEntry newTail = new LinkedEntry (key, value, hash, next, header, oldTail); return oldTail.nxt = header.prv = newTail; } //获取元素 @Override public V get(Object key) { /* * This method is overridden to eliminate the need for a polymorphic * invocation in superclass at the expense of code duplication. */ if (key == null) { HashMapEntry e = entryForNullKey; if (e == null) return null; if (accessOrder) makeTail((LinkedEntry ) e); return e.value; } // Replace with Collections.secondaryHash when the VM is fast enough (http://b/8290590). int hash = secondaryHash(key); HashMapEntry [] tab = table; for (HashMapEntry e = tab[hash & (tab.length - 1)]; e != null; e = e.next) { K eKey = e.key; if (eKey == key || (e.hash == hash && key.equals(eKey))) { if (accessOrder) makeTail((LinkedEntry ) e); return e.value; } } return null; } //将head ->pre指向最新操作的元素 private void makeTail(LinkedEntry e) { // Unlink e e.prv.nxt = e.nxt; e.nxt.prv = e.prv; // Relink e as tail LinkedEntry header = this.header; LinkedEntry oldTail = header.prv; e.nxt = header; e.prv = oldTail; oldTail.nxt = header.prv = e; modCount++; } //移除后,将引用全部去掉 @Override void postRemove(HashMapEntry e) { LinkedEntry le = (LinkedEntry ) e; le.prv.nxt = le.nxt; le.nxt.prv = le.prv; le.nxt = le.prv = null; // Help the GC (for performance) } //是否包括value @Override public boolean containsValue(Object value) { if (value == null) { for (LinkedEntry header = this.header, e = header.nxt; e != header; e = e.nxt) { if (e.value == null) { return true; } } return false; } // value is non-null for (LinkedEntry header = this.header, e = header.nxt; e != header; e = e.nxt) { if (value.equals(e.value)) { return true; } } return false; } //清空,然后将引用去掉 public void clear() { super.clear(); // Clear all links to help GC LinkedEntry header = this.header; for (LinkedEntry e = header.nxt; e != header; ) { LinkedEntry nxt = e.nxt; e.nxt = e.prv = null; e = nxt; } header.nxt = header.prv = header; } 我们现在在来分析下节点数据 LinkedHashMap1.LinkedEntry是继承HashMapEntry,并且还是添加到hashmap的tab中, 2. LinkedHashMap对添加元素,获取元素,删除元素,清空,进行了重写,目的是双向链表的链表建立和去除,保证元素的引用及时改变,帮助 GC回收。 3。 每次添加,修改,获取元素时,都将header->pre指向被操作的元素,这样就保证了排序 LinkedHashMap结构图: 今天就到这里吧,谢谢。

    转载请注明原文地址: https://ju.6miu.com/read-1299374.html
    最新回复(0)