LinkedHashMap分析

LinkedHashMap在java1.6和1.7中的源码几乎一样,就不一一比较了,这里就以1.7为例。其次LinkedHashMap是HashMap的子类,所有很多方法就直接采用HashMap中的实现,所以在研究LinkedHashMap之前首先需要搞明白HashMap的实现。

1 LinkedHashMap简介

LinkedHashMap是HashMap的子类,众所周知LinkedHashMap在咋们印象中是顺序存放的,其实数据的保存还是跟HashMap的数据保存一模一样的,只不过LinkedHashMap在类中自己维护了一个指针,这个指针的实现就跟java1.6中LinkedList一样,维护了的是一个保存数据的头指针。同样这个链表是一个双向链表。

// LinkedHashMap 的数据头指针
private transient Entry<K,V> header;

同时还有一个属性 accessOrder,默认是false,可以通过构造方法指定

// 这个属性如果为true,那么就会按照最近最少访问进行排序
private final boolean accessOrder;

换句话说就是,如果 accessOrder为true的时候,当你访问了一个元素,那么这个元素就会排在链表的最后,以此类推。。最少访问的就在最前面,那么这个属性就是按照这种规则进行排序的。。后面会对此属性进行源码分析。

2 初始化

2.1 构造函数

构造函数都是采用了super来引用了HashMap中的构造方法,所以没什么难度。唯一不同就是对 accessOrder进行了赋值,这个属性构造方法默认全部是false,除非通过构造函数自己指定。

LinkedHashMap<String, String> linkedMap = new LinkedHashMap<String, String>(16, .75f, true);

2.2 init()方法

不指导大家在研究HashMap的时候,在HashMap的构造函数中有一个 init() 方法,这个方法在HashMap是空函数,但是在LinkedHashMap却得到了实现,所以在调用构造函数的同时也会调用这个init()方法。

// 初始化头指针
void init() {
      header = new Entry<>(-1, null, null, null);
      header.before = header.after = header;
}

3 操作元素

3.1 添加元素

LinkedHashMap在添加元素的时候内部会调用addEntry()方法,同样LinkedHashMap不仅仅要HashMap中的数据,同时还要维护自己的双向链表。

void addEntry(int hash, K key, V value, int bucketIndex) {
        // 调用父类中的方法
        super.addEntry(hash, key, value, bucketIndex);
        // Remove eldest entry if instructed
        // 这个下面介绍
        Entry<K,V> eldest = header.after;
        if (removeEldestEntry(eldest)) {
            removeEntryForKey(eldest.key);
        }
}
// 这个方法在父类中的 addEntry() 方法中调用了
void createEntry(int hash, K key, V value, int bucketIndex) {
        HashMap.Entry<K,V> old = table[bucketIndex];
        Entry<K,V> e = new Entry<>(hash, key, value, old);
        table[bucketIndex] = e;
        // 维护自己的双向链表,将创建的数据插在头指针的前面,即整个链表的最后
        e.addBefore(header);
        size++;
}

代码中很明确,在维护HashMap的数据同时也对LinkedHashMap中的双向链表数据进行了维护。
这里还有一个方法 removeEldestEntry(),这个方法的参数是header后面的数据,即当前最旧的数据。

protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
      return false;
}

这个方法在LinkedHashMap中使用返回false,那这个protected方法有什么意义呢?这里就有一个就有一个设计思想,比如当你设计的LinkedHashMap中最大只能存在100个元素,那么这个方法就起作用了。

private static final int MAX_ENTRIES = 100;
// 覆盖这个方法, 当超过100个元素返回true, 然后代码中删除最旧的元素
protected boolean removeEldestEntry(Map.Entry eldest) {
     return size() > MAX_ENTRIES;
}

3.2 获取元素

获取元素比较简单,但是LinkedHashMap做了自己的实现。

public V get(Object key) {
     // 通过父类中的方法获取值
     Entry<K,V> e = (Entry<K,V>)getEntry(key);
     if (e == null)
         return null;
     // 这个方法 accessOrder 属性就起作用了 
     e.recordAccess(this);
     return e.value;
}

recordAccess的方法实现是,如果 accessOrder 属性为true,那么久将当前访问过的元素删除并插入到双向链表的最后。

void recordAccess(HashMap<K,V> m) {
     LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
     // 如果为 true 执行
     if (lm.accessOrder) {
         lm.modCount++;
         remove();
         addBefore(lm.header);
     }
}

3.3 迭代元素

LinkedHashMap之所以迭代出来的元素是有序的,是因为keySet()方法和entrySet()方法都做了自己的实现,虽然所有的内部类都和HashMap中一样,只不过获取的数据方式变了,LinkedHashMap是从自己的双向链表中获取的。。。具体就不再表述了,看下源码其实很简单。

4 其他方法

4.1 containsValue(Object value)

public boolean containsValue(Object value) {}

这个方法还是考虑的很周到的,覆盖了父类中的方法,不再和HashMap中一样从Hash表中去循环迭代元素,而是从自己的双向链表中去迭代。这个就提高了访问的效率。

4.2 clear()方法

不说了,太简单。

public void clear() {
     super.clear();
     header.before = header.after = header;
}

5 总结

总体来说LinkedHashMap继承了HashMap,之所以有序是因为自己内部维护了一个双向链表,其他也没有就和HashMap基本类似。
同样和HashMap一样的是,当学完LinkedHashMap之后,LinkedHashSet基本就没必要看了,实现大部分代码都是采用了LinkedHashMap中的实现。
LinkedHashSet同样也继承自HashSet,HashSet在构造方法的参数中有一个 dummy 布尔参数,如果传入该值,那么HashSet就构造一个LinkedHashMap的实例。其他属于LinkedHashSet自己的代码基本没有。。。

public LinkedHashSet() {
    super(16, .75f, true);
}
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor);
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值