LinkedHashMap

1、特点:
(1)、键有序:可选插入有序或者访问有序 《《《《《主要特点:默认插入有序
(2)、以键值对<k,v>的形式储存
(3)、键不能重复,如果重复,新的值会覆盖旧的值;
(4)、键可以为有一个为null,值可以多个为null;
特定:键为null的元素放在数组0号位置;
(5)、底层数组的容量为2的指数级
2、数据结构:数组+链表
底层虽然使用的仍然是数组+链表。可是LinkedHashMap底层也使用了双向链表来使元素保持有序,当然,只是键的有序;
3、底层源码分析:
(1)继承关系:
继承 HashMap<K,V>类; 实现 Map<K,V>接口
(2)构造函数:(5个)
注意点1:LinkedHashMap的五个构造方法都调用了其父类HashMap的构造方法;
注意点2:可以传入boolean值来选择使数据保持插入顺序或者访问顺序
(3)默认值 :
默认初始容量:16,
默认初始负载因子:0.75 ,
默认排序模式:false(插入有序)
(4)增长方式:
扩容临界: 当前size >= 扩容阈值(当前容量 * 负载因子)
resize( ) : 2倍扩容
(5)基本属性:
继承HashMap,,具有hashMap的所有属性;
新增属性:
Entry<K,V> header ; 头结点,实际上此节点在链表的尾部;
此头结点不储存数据,只做头结点用,调用LinkedHashMap构造方法的时候就已经使用init方法初始化;

  void init() {
        header = new Entry<K,V>(-1, null, null, null);
        header.before = header.after = header;
    }

boolean accessOrder ; 排序模式
true 遍历时使用访问顺序
false 遍历时使用插入顺序

内部类Entry{ }中也多了两个属性:
Entry after; 后一个节点
Entry before;前一个节点
(6)增添查改
A、添加元素**************************************
put方法:LinkefHashMap并没有自己实现put方法,而是直接使用的父类HashMap的put方法,其过程分析如下:
直接调用父类的put方法添加元素:父类方法 put
put方法中调用重写的addEntry方法或者recordAccess方法;本类方法 addEntry,recordAccess
addEntry方法会调用父类的addEntry方法:为了数组扩容和重新哈希
父类addEntry会调用当前子类的createEntry方法:添加元素
然后调用addBefore方法 :使新元素保存上一个元素的索引,并将新元素的索引保存在header中;

(父类HashMap实现)
public V put(K key, V value) {
    if (table == EMPTY_TABLE) {
        inflateTable(threshold);
    }
    if (key == null)
        return putForNullKey(value);
    int hash = hash(key);
    int i = indexFor(hash, table.length);
    for (Entry<K,V> 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++;
    //此处调用的是子类LinkedHashMap的重写方法
    addEntry(hash, key, value, i);
    return null;
}
(子类LinkedHashMap实现)
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;
    //此处的removeELDESTentry方法是LinkedHashMap的方法
    if (removeEldestEntry(eldest)) {
        removeEntryForKey(eldest.key);
    }
}
(父类HashMap实现)
void addEntry(int hash, K key, V value, int bucketIndex) {
    if ((size >= threshold) && (null != table[bucketIndex])) {
        resize(2 * table.length);
        hash = (null != key) ? hash(key) : 0;
        bucketIndex = indexFor(hash, table.length);
    }
    createEntry(hash, key, value, bucketIndex);
}

(子类LinkedHashMap实现)
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;
//此处addBrfore方法的入参永远为header,因为header中保存上一个节点的索引
    e.addBefore(header);
    size++;
}

LinkedHashMap的核心思想:为了是元素有序,便定义了一个header节点由于记录每
个节点的索引,然后依次将head节点中保存的节点信息赋值到新加入的节点的 after
属性和before属性上,实现了键有序;

//实际上就是把当前元素插到尾节点的前面
(当前类LinkedHashMap实现)
private void addBefore(Entry<K,V> existingEntry) {
    after  = existingEntry;
    before = existingEntry.before;
    before.after = this;
    after.before = this;
}

4、与hashMap不同点:
LinkedHashMap其中的内部类Entry有多出了Entry<K,V> before, after,而此举就是为了实现数据的有序性,为了根据插入顺序将节点连接起来(双向链表)
5、存在的必要性:
可以在遍历时有序;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值