• java容器类:LinkedHashMap


    LinkedHashMap基于HashMap实现,继承了HashMap.,存储数据的方式和HashMap一致(将键值对映射为entry对象).

    • 不同之处
      LinkedHashMap还在内部维护了一个链表.
      LinkedHashMap的内部数据结构类Entry继承了HashMap的Entry类,额外添加了before节点和after节点,还额外实现了remove,addBefore,recordAccess,recordRemoval方法用于构造链表

    这个链表是怎么形成的?#

    1.初始化链表头##

    LinkedHashMap在实例化的时候会指定超类的构造器并进行实例化,超类实例化时会以多态的方式(动态绑定)来调用LinkedHashMap中重写的init方法,init方法被用来初始化链表的头结点.

    public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
    
        this.loadFactor = loadFactor;
        threshold = initialCapacity;
        init()//HashMap中的init()方法是空的,但是子类继承该类后通过重写的方式实现init方法.HashMap类可以说一个基础实现,它有几个方法都是空的,是为子类实现做准备.
    }
    
    @Override
    void init() {
        header = new Entry<>(-1, null, null, null);//构造一个空的entry
        header.before = header.after = header;//头节点和尾节点都指向自身.
    }
    

    2.向链表中添加数据##

    • LinkedHashMap中还有一个关键的布尔类型变量accessOrder,默认为false,值为false时,向LinkedHashMap插入数据其实是调用HashMap的put方法.
    1. 对于put方法如果,key已经在HashMap中,那么仅仅用新的值替换旧的值并返回旧的值.
    2. 反之就插入一个新的键值对,即在HashMap中构造并加入一个新的entry.
    3. 那么插入一个entry实质上是调用addEntry方法,该方法不仅在HashMap中有实现,在LinkedHashMap中亦有实现.LinkedHashMap的实现在父类的基础上更加入了将entry插入链表的操作.
    4. 所以LinkedHashMap插入数据时不仅向HashMap的table数组中插入一个entry,而且将该entry放入了LinkedHashMap自身实现的链表中.
    5. 值为true时,会在LinkedHashMap执行put,putAll,get操作时,改变链表结构,将最近访问的entry放在链表的尾部.
    //HashMap的addEntry方法
    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);
    }
    
    
    //HashMap上调用的相应的方法都会被重写
    //LinkedHashMap的addEntry方法
    void addEntry(int hash, K key, V value, int bucketIndex) {
        super.addEntry(hash, key, value, bucketIndex);//在HashMap的基础上更完善了自身的功能
    
        // Remove eldest entry if instructed
        Entry<K,V> eldest = header.after;
        if (removeEldestEntry(eldest)) {//removeEldestEntry可以由我们自己来重写,下面将会实现该效果
            removeEntryForKey(eldest.key);
        }
    //LinkedHashMap的createEntry方法
    void createEntry(int hash, K key, V value, int bucketIndex) {
        HashMap.Entry<K,V> old = table[bucketIndex];//对应位置的Entry被提取出来
        Entry<K,V> e = new Entry<>(hash, key, value, old);//加入新的Entry的末尾
        table[bucketIndex] = e;//新的Entry加入该位置
        e.addBefore(header);//调用LinkedHashMap内部Entry类的方法
        size++;
    }
    
    private void addBefore(Entry<K,V> existingEntry) {
            after  = existingEntry;        //header被设为新的Entry的尾节点
            before = existingEntry.before; //header的头结点被设为新的Entry的头结点,实际上header被初始化后,它的头结点和尾节点都为自身
            before.after = this;    //把新的Entry的节点同样地设为header的头结点和尾节点
            after.before = this;    //可以理解为两个类形成一个环形链.
        }
    //
    

    执行一次put操作,LinkHashMap中执行步骤




    每个数据的插入以此类推

    前面说到accessOrder值为true时,会在LinkedHashMap执行put,putAll,get操作时,改变链表结构,将最近访问的entry放在链表的尾部.
    起作用的方法是Entry内实现的recordAccess方法,在执行get,put,putAll方法都能调用到该方法

    void recordAccess(HashMap<K,V> m) {
            LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
            if (lm.accessOrder) {
                lm.modCount++;
                remove();    //断开与头结点和尾节点的连接
                addBefore(lm.header);//将Entry加到链表的尾部
            }
        }
    
     private void remove() {
            before.after = after;
            after.before = before;
        }
    

    用代码举例

       LinkedHashMap<String, String> a = new LinkedHashMap<String, String>(16, 0.75f, true);//accessOrder为true
       LinkedHashMap<String, String> b = new LinkedHashMap<String, String>(16, 0.75f, false);//accessOrder为false
    	//a和b都以相同顺序加入键值对,都调用get方法,然后查看通过Iterator输出的值
                a.put("1", "1");
    	a.put("2", "2");
    	a.put("3", "3");
    	b.put("1", "1");
    	b.put("2", "2");
    	b.put("3", "3");
    	a.get("1");
    	b.get("1");
    	Iterator<Entry<String, String>> i = a.entrySet().iterator();
    	Iterator<Entry<String, String>> m = b.entrySet().iterator();
    	
    	while(i.hasNext()) {
    		System.out.println("a输出的值:"+i.next().getValue());
    	}
    	while(m.hasNext()) {
    		System.out.println("b输出的值:"+m.next().getValue());
    	}
    

    a输出的值:2
    a输出的值:3
    a输出的值:1
    b输出的值:1
    b输出的值:2
    b输出的值:3
    因为1被调用过,所以被放到了最后输出

    在addEntry方法中还有一个可以让我们拓展的功能

     // Remove eldest entry if instructed
        Entry<K,V> eldest = header.after;
        if (removeEldestEntry(eldest)) {
            removeEntryForKey(eldest.key);
        }
    
    //默认返回false,我们可以重写这个方法
    protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
        return false;
    }
    
    LinkedHashMap<String, String> c = new LinkedHashMap<String, String>(){
    		private static final int MAX_ENTRIES = 2;
    	     
              protected boolean removeEldestEntry(Map.Entry eldest) {
                 return size() > MAX_ENTRIES;
              }
    	};
    
     c.put("1", "1");
     c.put("2", "2");
     c.put("3", "3"); 
    
    Iterator<Entry<String, String>> n = c.entrySet().iterator();
         while(n.hasNext()) {
    			System.out.println("c输出的值:"+n.next().getValue());
    		}
    }
    

    c输出的值:2
    c输出的值:3

    • c中值允许存在MAX_ENTRIES数量的键值对
    • 再向其中添加会把最初添加的键值对删除.换句话说,map中只会存在最新的entry
  • 相关阅读:
    Algs4-1.1.17找出以下递归函数的问题
    Algs4-1.1.16给出exR1(6)的返回值
    Algs4-1.1.15编写一个静态方法histogram()
    Algs4-1.1.14实现以2为底的对数
    Algs4-1.1.13编写一段代码,打印出一个M行N列的二维数组的转置(交换行和列)
    Algs4-1.1.12以下代码段会打印出什么结果?
    python将一个列表的元素随机打乱
    python 类似java的三目运算符
    java的三元运算符
    java 的Colections类(Java也有python类似列表的反转、排序等方法)
  • 原文地址:https://www.cnblogs.com/itsrobin/p/5140522.html
Copyright © 2020-2023  润新知