Android LinkedHashMap

1.LinkedHashMap

LinkedHashMap是带链表的HashMap,所以LinkedHashMap是有序的,它作为HashMap的扩展,改变了HashMap无序的特征。LinkedHashMap使用了一个双向链表来维护key-value对的次序,该链表维护了map的迭代顺序,该迭代顺序和key-value对的插入顺序保持一致。

LinkedHashMap的数据结构图:

e72b9158b1f64701a65bca06330121f2.gif

 对于LinkedHashMap而言,它继承与HashMap、底层使用哈希表与双向链表来保存所有元素。其基本操作与父类HashMap相似,它通过重写父类相关的方法,来实现自己的链接列表特性。根据链表中元素的顺序可以分为:按插入顺序的链表和按访问顺序(调用get方法)的链表。

LinkedHashMap定义了排序模式accessOrder,该属性为boolean型变量,对于访问顺序,为true;对于插入顺序,则为false。

当有新元素加入Map的时候会调用Entry的addEntry方法,会调用removeEldestEntry方法,这就是实现LRU元素过期机制的地方,默认的情况下removeEldestEntry方法只返回false表示元素永远不过期。

2.LinkedHashMap用法

HashMap<String,String> map = new LinkedHashMap<String, String>(5, 0.5f, true) {

    @Override

    protected boolean removeEldestEntry( Map.Entry<String, String> eldest){

        if(size() > 5){

            return true;

        }

        return false;

    } 

};

map.put("a", "a");

map.put("b", "b");

map.put("c", "c");

map.put("d", "d");

map.put("e", "e");

map.put("c", "c");

for (Map.Entry<String, String> entry : map.entrySet()) {  

    System.out.print(entry.getValue() + ", ");  

}  

System.out.println();

map.get("b");

for (Map.Entry<String, String> entry : map.entrySet()) {  

    System.out.print(entry.getValue() + ", ");  

}  

System.out.println();

map.put("f", "f");

for (Map.Entry<String, String> entry : map.entrySet()) {  

    System.out.print(entry.getValue() + ", ");  

}  

System.out.println();

打印结果:

a, b, d, e, c,

a, d, e, c, b,

d, e, c, b, f,

可见最新加入的或者最近get过的就会往最后放,如果put数目大于max,就会把头上那个去掉,然后f加到最后,其他往前移。

所以说不只是put,get也会把Entry往后方,越往后的说明最近才访问过的,越往前的说明最近最少访问的,这就实现了一个简单的LRU。

3.LinkedHashMap的方法

(1)构造方法

LinkedHashMap的构造方法,都是通过调用父类的构造方法来实现,大部分accessOrder默认为false。

public LinkedHashMap(int initialCapacity, float loadFactor) {

    super(initialCapacity, loadFactor);

    accessOrder = false;

}

public LinkedHashMap(int initialCapacity) {

    super(initialCapacity);

    accessOrder = false;

}

public LinkedHashMap() {

    super();

    accessOrder = false;

}

public LinkedHashMap(Map extends K, ? extends V> m) {

    super(m);

    accessOrder = false;

}

public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {

    super(initialCapacity, loadFactor);

    this.accessOrder = accessOrder;

}

上面是LinkedHashMap的构造方法,通过传入初始化参数和代码看出,LinkedHashMap的构造方法和父类的构造方法,是一一对应的。也是通过super()关键字来调用父类的构造方法来进行初始化,唯一的不同是最后一个构造方法,提供了AccessOrder参数,用来指定LinkedHashMap的排序方式,accessOrder =false -> 插入顺序进行排序 , accessOrder = true -> 访问顺序进行排序。

(2)put()方法

LinkedHashMap并没有重写父类的put()方法,说明调用put方法时实际上调用的是父类的put方法。

(3)get()方法

public V get(Object key) {

    Entry e = (Entry)getEntry(key); //调用父类的getEntry()方法

    if (e == null) return null;

    e.recordAccess(this); //判断排序方式,如果accessOrder = true , 删除当前e节点

    return e.value;

}

(4)remove()

LinkedHashMap并没有重写父类的remove()方法,说明调用remove方法时实际上调用的是父类的remove()方法。

3.源码解析Entry定义

private static class Entry extends HashMap.Entry {

    //定义Entry类型的两个变量,或者称之为前后的两个指针

    Entry before, after;

    //构造方法与HashMap的没有区别,也是调用父类的Entry构造方法

    Entry(int hash, K key, V value, HashMap.Entry next) {

        super(hash, key, value, next);

    }

    //删除

    private void remove() {

        before.after = after;

        after.before = before;

    }

    //插入节点到指定的节点之前

    private void addBefore(Entry existingEntry) {

        after = existingEntry;

        before = existingEntry.before;

        before.after = this;

        after.before = this;

    }

    //方法重写,HashMap中为空

    void recordAccess(HashMap m) {

        LinkedHashMap lm = (LinkedHashMap)m;

        if (lm.accessOrder) {

            lm.modCount++;

            remove();

            addBefore(lm.header);

        }

    }

    //方法重写 ,HashMap中方法为空

    void recordRemoval(HashMap m) {

        remove();

    }

}

public class LinkedHashMap extends HashMap implements Map{

    private static final long serialVersionUID = 3801124242820219131L; //可序列化版本号

    //双向链表的头指针

    private transient Entry header;

    //双向链表的排序方法,false 插入顺序排序,true访问顺序排序

    private final boolean accessOrder;

    //构造方法,指定初始大小,指定负载因子

    public LinkedHashMap(int initialCapacity, float loadFactor) {

        super(initialCapacity, loadFactor);

        accessOrder = false;

    }  

    //构造方法,指定初始容量

    public LinkedHashMap(int initialCapacity) { super(initialCapacity);

        accessOrder = false;

    }

    //无参构造方法k,采用默认参数

    public LinkedHashMap() {

        super();

        accessOrder = false;

    }

    //将指定的m集合转化为LinkedHashmap存储

    public LinkedHashMap(Map extends K, ? extends V> m) {

        super(m);

        accessOrder = false;

    }

    //构造方法,指定初始容量,负载因子和排序方式

    public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {

        super(initialCapacity, loadFactor); this.accessOrder = accessOrder;

    }

    //重写父类init方法,init方法在父类构造函数被调用,初始化双向链表。header的前驱和后继都是指向它自己

    @Override

    void init() {

        header = new Entry<>(-1, null, null, null);

        header.before = header.after = header;

    }    

    //重写父类的transfer方法,在HashMap执行扩容操作时被调用,HashMap中的是通过遍历Entry[]数组的方式来实现数据的拷贝复制,重写后是通过遍历双向链表的方式来进行数据的复制。遍历双向链表的方式效率上更高一些

    @Override

    void transfer(HashMap.Entry[] newTable, boolean rehash) {

        int newCapacity = newTable.length;

        for (Entry e = header.after; e != header; e = e.after) {

            if (rehash)

                e.hash = (e.key == null) ? 0 : hash(e.key); int index = indexFor(e.hash, newCapacity);

            e.next = newTable[index];

            newTable[index] = e;

        }

    } 

    //重写父类的containsValue, 由遍历数组的方式修改为遍历列表的方式

    public boolean containsValue(Object value) {

        if (value==null) {

            for (Entry e = header.after; e != header; e = e.after)

                if (e.value==null) return true;

        } else {

            for (Entry e = header.after; e != header; e = e.after)

                if (value.equals(e.value)) return true;

        }

        return false;

    }    

    //重写父类的get方法

    public V get(Object key) {

        Entry e = (Entry)getEntry(key); //返回实体

        if (e == null) return null;

        e.recordAccess(this); //如果是访问顺序排序,则将e移动到链表的末尾处

        return e.value;

    }

   //清除集合

    public void clear() {

        super.clear();

        header.before = header.after = header;

    }

    //内部类实现了迭代方法

    private abstract class LinkedHashIterator implements Iterator {

        Entry nextEntry = header.after;

        Entry lastReturned = null;

        int expectedModCount = modCount;

        public boolean hasNext() {

            return nextEntry != header;

        }

        public void remove() {

            if (lastReturned == null)

                throw new IllegalStateException();

            if (modCount != expectedModCount)

                throw new ConcurrentModificationException();

            LinkedHashMap.this.remove( lastReturned.key);

            lastReturned = null;

            expectedModCount = modCount;

        }

        Entry nextEntry() {

            if (modCount != expectedModCount)

                throw new ConcurrentModificationException();

            if (nextEntry == header)

                throw new NoSuchElementException();

            Entry e = lastReturned = nextEntry;

            nextEntry = e.after;

            return e;

        }

    }  

    private class KeyIterator extends LinkedHashIterator {

        public K next() {

            return nextEntry().getKey();

        }

    }

    private class ValueIterator extends LinkedHashIterator {

        public V next() {

            return nextEntry().value;

        }

    }

    private class EntryIterator extends LinkedHashIterator> {

        public Map.Entry next() {

            return nextEntry();

        }

    }

    Iterator newKeyIterator() {

        return new KeyIterator();

    }

    Iterator newValueIterator() {

        return new ValueIterator();

    }

    Iterator> newEntryIterator() {

        return new EntryIterator();

    }

    //重写父类的addEntry方法

    void addEntry(int hash, K key, V value, int bucketIndex) {

        super.addEntry(hash, key, value, bucketIndex);

        Entry eldest = header.after;

        if (removeEldestEntry(eldest)) {

            removeEntryForKey(eldest.key);

        }

    }

    //重写createEntry方法。执行两步操作:1. 添加到table数组中, 2 . 插入到双向链表中

    void createEntry(int hash, K key, V value, int bucketIndex) {

        HashMap.Entry old = table[bucketIndex];

        Entry e = new Entry<>(hash, key, value, old);

        table[bucketIndex] = e;

        e.addBefore(header);

        size++;

    }

    protected boolean removeEldestEntry( Map.Entry eldest) {

        return false;

    }

}

LinkedHashMap继承了HashMap类,重写了部分方法,在HashMap中一些空的实现,LinkedHashMap都做了实现,扩展了HashMap类的功能,LinkedHashMap可以保存元素的插入顺序,顺序有两种方式一种是按照插入顺序排序,一种按照访问做排序。默认以插入顺序排序,性能比HashMap略低,线程也是不安全的。

猜你喜欢

转载自blog.csdn.net/zenmela2011/article/details/125231389