Java集合源码分析10----LinkedHashMap源码分析

简介

    LinkedHashMap自jdk1.4引入,继承了HashMap,实现了接口Map,与HashMap具有相同数据结构,底层是哈希表结构,即数组+单向链表(或红黑树)。但LinkedHashMap在HashMap维护节点关系的基础上,将所有的节点串起来,形成了双向链表。所以存储到LinkedHashMap中元素能够保持一定的顺序。默认情况下,迭代时返回元素顺序是插入的顺序。另外一种顺序是访问顺序,最近访问过的节点将被调整到链表的末尾。
    LinkedHashMap的线程不安全的,允许存储null值和null键 。

                                                 

    如图,LinkedHashMap的底层是哈希桶数组,数组中存储的是单向链表或者红黑树,内部维护的双向链表会将所有的节点串起来,头结点是head,尾节点是tail。LinkedHashMap中的节点Entry继承了HashMap.Node,添加到哈希桶数组中指定索引位置的元素,通过next来维护桶中单向链表。在Node基础上添加到两个变量before和after,维护了LinkedHashMap的内部的双向链表。 

static class Entry<K,V> extends HashMap.Node<K,V> {
	Entry<K,V> before, after;
	Entry(int hash, K key, V value, Node<K,V> next) {
	    super(hash, key, value, next);
	}
}

    LinkedHashMap的大多方法继承自父类,没有重写,本文不对HashMap中添加、修改、删除节点的方法全部代码分析,可以参照HashMap源码分析。

介绍

1.构造方法

    //构建一个指定的初始容量和负载因子的LinkedHashMap,元素顺序是插入的顺序
    //指定的初始容量是负数或者负载因子是非正数,将抛出IllegalArgumentException异常
    public LinkedHashMap(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor);
        accessOrder = false;
    }

    //构建具有指定初始容量和负载因子为默认值0.75的LinkedHashMap,元素顺序是插入的顺序。
    //如果初始容量为负值,将抛出IllegalArgumentException异常
    public LinkedHashMap(int initialCapacity) {
        super(initialCapacity);
        accessOrder = false;
    }

    //构建一个默认初始容量为61和负载因子为0.75的LinkedHashMap,元素顺序是插入的顺序。
    public LinkedHashMap() {
        super();
        accessOrder = false;
    }

    //构建与指定映射集map具有相同键-值对映射的LinkedHashMap,元素顺序是插入的顺序。
    //LinkedHashMap创建时具有默认的负载因子和足够的初始容量,以容纳指定map的所有的键-值对映射。
    //如果指定映射集map为null,将抛出NullPointerException异常
    public LinkedHashMap(Map<? extends K, ? extends V> m) {
        super();
        accessOrder = false;
        putMapEntries(m, false);
    }

    //构建具有指定初始容量和负载因子,排序模式的LinkedHashMap.
    //如果初始容量为负数或者负载因子是非正数,将抛出IllegalArgumentException异常
    //当accessOrder为true时,访问顺序(access-order);accessOrder为false时,插入顺序(insertion-order)
    public LinkedHashMap(int initialCapacity,
                         float loadFactor,
                         boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
    }

    LinkedHashMap的构造函数有五种,包含了一种无参构造方法和四种有参构造方法。而大部分的构造方法都会访问父类HashMap的构造方法。有参构造方法可以设置初始容量initialCapacity和加载因子loadFactor,以及元素迭代顺序accessOrder。默认的accessOrder是false,表示迭代元素返回顺序是插入元素的顺序。可以调整初始容量和加载因子来调整LinkedHashMap的性能,一般情况下,如果对参数没有特别的要求情况下,推荐使用默认值。

2.内部变量

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

//双向链表的头节点(最老的)
transient LinkedHashMap.Entry<K,V> head;

//双向链表的尾节点(最年轻的)
transient LinkedHashMap.Entry<K,V> tail;

//LinkedHashMap迭代排序的方法,true表示的是访问顺序(access-order),false表示的是插入顺序(insertion-order)
final boolean accessOrder;

    LinkedHashMap内部维护的是双向链表,而head和tail分别指向链表的头结点和尾节点。当链表中只有一个节点时,head和tail指向该节点,每次向链表中添加节点时,新的节点会添加到了tail的后面,然后tail会移动到新添加的节点上。

    accessOrder的值可以在构造方法中指定,表示LinkedHashMap的迭代顺序,当accessOrder为false时,迭代元素返回的顺序是插入元素顺序,而accessOrder为true时,迭代元素返回的顺序是访问顺序。例如:向LinkedHashMap中先后存入A,B,C三个元素(键-值对映射),当accessOrder=false时,迭代元素返回的顺序是A,B,C。当accessOrder=true,如果先访问A元素,A所在的节点便会移动到链表末尾,迭代返回顺序B,C,A。与访问元素的次数没有关系的,最近访问的元素所在节点成为链表的尾节点。

方法分析

    关于LinkedHashMap中的方法需要说明的是,除了少数几个方法重写了父类HashMap中的方法,如get()方法,而其他方法直接继承父类。在HashMap源码中不难发现,有几个方法在HashMap是空实现,这在子类LinkedHashMap进行了实现,此类方法有afterNodeAccess(Node<K,V> p)afterNodeInsertion(boolean evict)afterNodeRemoval(Node<K,V> p)。下面对这些方法进行分析。

1.添加节点

    LinkedHashMap没有重写父类HashMap中的put()方法,向LinkedHashMap中添加键-值对映射时,会先调用put()方法,put()核心方法是putVal()方法,而在putVal()中新建节点的方法newNode(),以及afterNodeAccess(e)以及afterNodeInsertion(evict)几个方法,在LinkedHashMap中进行了重写和实现。

//重写了父类中的方法
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
    LinkedHashMap.Entry<K,V> p =
        new LinkedHashMap.Entry<K,V>(hash, key, value, e);
    //生成节点p后,需要将节点与双向链表做到关联
    linkNodeLast(p);
    return p;
}
// link at the end of list
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
  //先将上次尾节点赋值为最后一个节点
    LinkedHashMap.Entry<K,V> last = tail;
    //现在p节点为尾节点
    tail = p;
    //如果之前的尾节点为null,之前不存在节点,添加p变成了头结点
    if (last == null)
        head = p;
    else {
      //如果之前存在尾节点,p的前驱指针指向之前尾节点,之前尾节点后驱指针指向p节点
        p.before = last;
        last.after = p;
    }
}

    新建节点的方法newNode()在LinkedHashMap中进行了重写,向LinkedHashMap添加键-值对映射,会先调用父类HashMap中的put方法将节点存储到哈希桶中,然后将新增的节点先转成双向链表的节点,调用linkNodeLast()方法将节点添加到LinkedHashMap内部维护的双向链表末尾。

    另外一个方法afterNodeInsertion(e)也会在putVal()中调用,主要功能是否移除存在时间最久的一个节点,从源码可以看到afterNodeInsertion(e)核心部分代码不会执行,因为在LinkedHashMap源码中removeEldestEntry()方法返回的总是false,当然并不是没有用处的,此处经常用于缓存策略,当使用LinkedHashMap实现缓存时,通过重写removeEldestEntry()方法,删除存在时间最久节点。

//插入一个节点后将存在时间最长的节点移除
void afterNodeInsertion(boolean evict) { // possibly remove eldest
    LinkedHashMap.Entry<K,V> first;
    //evict为true,在LinkedHashMap中removeEldestEntry总返回false,需要子类重写此方法才能往下执行
    if (evict && (first = head) != null && removeEldestEntry(first)) {
        K key = first.key;
        removeNode(hash(key), key, null, false, true);
    }
}

//如果此LinkedHashMap应该移除存在最久的键-值对,返回true.
//但在LinkedHashMap总返回false
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
    return false;
}

     此外在putVal()方法中进行了判断,当哈希桶数组中某个索引位置的节点超过了阈值TREEIFY_THRESHOLD = 8,会调用treeifyBin()方法将桶中的单向链表转换成红黑树,其中treeifyBin()方法会调用replacementTreeNode()方法将单向链表节点转换成树节点,这在LinkedHashMap中进行了重写。

//重写父类中的方法
TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {
  //将节点p强转为双向链表节点
    LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p;
    //生成一个树节点p
    TreeNode<K,V> t = new TreeNode<K,V>(q.hash, q.key, q.value, next);
    transferLinks(q, t);
    return t;
}

// apply src's links to dst
//将树节点转为链表节点,和链表节点转为树节点会调用此方法
//以链表节点转变为树节点来分析
private void transferLinks(LinkedHashMap.Entry<K,V> src,
                           LinkedHashMap.Entry<K,V> dst) {
  //双向链表中维护的前驱节点和后驱节点分别赋给树节点的前驱节点和后驱节点
    LinkedHashMap.Entry<K,V> b = dst.before = src.before;
    LinkedHashMap.Entry<K,V> a = dst.after = src.after;
    //b节点为null,新生成的树节点是头结点
    if (b == null)
        head = dst;
    else
      //将节点b的后驱节点指向生成的树节点
        b.after = dst;
    //b节点为null时,新生成的树节点是尾节点
    if (a == null)
        tail = dst;
    else
      //将节点a的前驱节点指向新生成的树节点
        a.before = dst;
}

    通过调用transferLinks()方法将红黑树中的节点维护到双向链表中,需要注意的是,不管底层的哈希桶数组中某个索引位置存储的是单向链表,还是当超过阈值转成红黑树,单向链表和红黑树中节点都会被维护到双向链表中。这也解释了LinkedHashMap与父类HashMap相比,元素是有序的原因。

2.删除节点

    LinkedHashMap删除键-值对映射时,会先调用继承自父类的方法remove(),remove()方法会调用removeNode()方法将节点从哈希桶数组中删除。另外在removeNode()方法中调用了afterNodeRemoval(),此方法在LinkedHashMap进行了实现,会将节点从LinkedHashMap内部维护的双向链表中进行删除。

//-----b<-->p<--->a
void afterNodeRemoval(Node<K,V> e) { // unlink
  //将当前节点强转为了双向链表节点,获取p节点的前驱节点和后驱节点
    LinkedHashMap.Entry<K,V> p =(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
    //将节点p中前驱指针和后驱指针置为null.
    p.before = p.after = null;
    //如果前驱节点为null,p节点被移除,后驱节点a变成了头节点
    if (b == null)
        head = a;
    else
      //将b节点的后驱节点指向a节点
        b.after = a;
    //如果a节点为null,表明原先p是尾节点,现在b变成了尾节点
    if (a == null)
        tail = b;
    else
      //否则将a节点的前驱节点指向b节点
        a.before = b;
}

3.查询节点

    LinkedHashMap对get()方法进行了重写,当LinkedHashMap中将accessOrder设置为true时,get()方法会调用afterNodeAccess(),此方法会将访问的节点移到双向链表的末尾,变成尾节点,这也解释了LinkedHashMap内部元素以访问顺序迭代时,最近访问的元素(键-值对映射)在最后原因。

//返回指定键的映射到的值,如果LinkedHashMap不包含键的映射,返回null.
//如果返回值为null,一种是不存在指定键的映射,一种是指定键与null的进行了映射,对于这种情况可以通过containKey来区分
public V get(Object key) {
    Node<K,V> e;
    if ((e = getNode(hash(key), key)) == null)
        return null;
    if (accessOrder)
        afterNodeAccess(e);
    return e.value;
}

//如果LinkedHashMap不包含指定键的映射,返回指定的默认值defaultValue
public V getOrDefault(Object key, V defaultValue) {
   Node<K,V> e;
   if ((e = getNode(hash(key), key)) == null)
       return defaultValue;
   if (accessOrder)
       afterNodeAccess(e);
   return e.value;
}

//将节点移动到最后
void afterNodeAccess(Node<K,V> e) { // move node to last
  LinkedHashMap.Entry<K,V> last;
  //如果accessOrder为true(访问顺序),尾节点不等于e
  if (accessOrder && (last = tail) != e) {
    //节点e强转成双向链表节点,并获取节点p的前驱节点,和后驱节点
      LinkedHashMap.Entry<K,V> p =
          (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
      //p成了尾节点,后驱节点必定是null
      p.after = null;
      //p的前驱节点为null,表明节点p是头节点,将p的后驱节点设置为头节点
      if (b == null)
          head = a;
      else
      //p不是头结点,p的前驱节点的后面节点设置为节点a。
          b.after = a;
      //p的后驱节点不为null,p不是尾节点,将p的后驱节点的上个节点设置为节点b
      if (a != null)
          a.before = b;
      else
        //p的后驱节点为null,表明p节点是尾节点,将p的前驱节点设置为last
          last = b;
      //如果last为null,表明双向链表中只有节点p,将p设置为头结点
      if (last == null)
          head = p;
      else {
        //将p节点与最后一个节点互相指向
          p.before = last;
          last.after = p;
      }
      //将p节点设置为尾节点
        tail = p;
        ++modCount;
    }
}

4.扩容方法

    这里不具体分析扩容的方法resize(),每次扩容都会重新计算哈希桶数组中节点的索引位置,原先哈希桶数组中某索引位置存储的是红黑树,扩容后节点的数量小于等于阈值UNTREEIFY_THRESHOLD = 6,会调用方法untreeify()将红黑树链表化,而在untreeify()方法中调用方法replacementNode(),此方法在LinkedHashMap中进行了实现,主要是功能是将红黑树节点转换成普通的节点。

Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) {
  //将节点p强转为双向链表节点q
    LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p;
    //生成一个双向链表节点
    LinkedHashMap.Entry<K,V> t =
        new LinkedHashMap.Entry<K,V>(q.hash, q.key, q.value, next);
    transferLinks(q, t);
    return t;
}

    此外向LinkedHashMap添加键-值对映射的核心方法putVal(),将节点存储到哈希桶数组中时,如果节点所在的索引位置存储的是红黑树,会调用putTreeVal()方法将节点存储到红黑树中,而其中生成树节点的方法newTreeNode(),在LinkedHashMap中进行了重写。会将新生成的节点添加到双向链表的末尾。

//重写父类中的方法
TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
    TreeNode<K,V> p = new TreeNode<K,V>(hash, key, value, next);
    linkNodeLast(p);
    return p;
}

5.其他方法

//如果LinkedHashMap将一个键或者多个键映射到指定的值,将返回true.
public boolean containsValue(Object value) {
  //遍历链表,如果链表中节点包含指定的值,返回true
    for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) {
        V v = e.value;
        if (v == value || (value != null && value.equals(v)))
            return true;
    }
    return false;
}
//返回此LinkedHashMap中键的集合Set
//此set集合支持键的移除操作(Iterator.remove,Set.remove,removeAll,retainAll,clear)
//不支持add或者addAll操作,移除操作将直接影响LinkedHashMap
public Set<K> keySet() {
    Set<K> ks = keySet;
    if (ks == null) {
        ks = new LinkedKeySet();
        keySet = ks;
    }
    return ks;
}

final class LinkedKeySet extends AbstractSet<K> {
  //键的数量
    public final int size()                 { return size; }
    //清空映射集合
    public final void clear()               { LinkedHashMap.this.clear(); }
    //返回迭代器
    public final Iterator<K> iterator() {
        return new LinkedKeyIterator();
    }
    //是否包含指定的键
    public final boolean contains(Object o) { return containsKey(o); }
    //移除指定的键
    public final boolean remove(Object key) {
        return removeNode(hash(key), key, null, false, true) != null;
    }
    public final Spliterator<K> spliterator()  {
        return Spliterators.spliterator(this, Spliterator.SIZED |
                                        Spliterator.ORDERED |
                                        Spliterator.DISTINCT);
    }
    //对于的键执行指定的操作action
    public final void forEach(Consumer<? super K> action) {
        if (action == null)
            throw new NullPointerException();
        int mc = modCount;
        //遍历链表
        for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
            action.accept(e.key);
        //检查并发修改情况
        if (modCount != mc)
            throw new ConcurrentModificationException();
    }
}

//返回此LinkedHashMap中值集合
//集合支持键的移除操作(Iterator.remove,Set.remove,removeAll,retainAll,clear),
//不支持add或者addAll操作。移除操作将会影响LinkedHashMap
public Collection<V> values() {
    Collection<V> vs = values;
    if (vs == null) {
        vs = new LinkedValues();
        values = vs;
    }
    return vs;
}

final class LinkedValues extends AbstractCollection<V> {
  //返回值的数量大小
    public final int size()                 { return size; }
    //清空映射集合
    public final void clear()               { LinkedHashMap.this.clear(); }
    //返回迭代值的迭代器
    public final Iterator<V> iterator() {
        return new LinkedValueIterator();
    }
    //值集合是否包含指定的值
    public final boolean contains(Object o) { return containsValue(o); }
    public final Spliterator<V> spliterator() {
        return Spliterators.spliterator(this, Spliterator.SIZED |
                                        Spliterator.ORDERED);
    }
    //对值集合中所有的值执行指定的操作action
    public final void forEach(Consumer<? super V> action) {
        if (action == null)
            throw new NullPointerException();
        int mc = modCount;
        for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
            action.accept(e.value);
        //检查并发性
        if (modCount != mc)
            throw new ConcurrentModificationException();
    }
}

//返回此LinkedHashMap中所有键-值对的集合Set
// 此set集合支持键的移除操作(Iterator.remove,Set.remove,removeAll,retainAll,clear),
 //不支持add或者addAll操作。移除操作将直接影响LinkedHashMap
public Set<Map.Entry<K,V>> entrySet() {
    Set<Map.Entry<K,V>> es;
    return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es;
}

final class LinkedEntrySet extends AbstractSet<Map.Entry<K,V>> {
  //返回键-值对映射的数量
    public final int size()                 { return size; }
    //清空键-值对映射集
    public final void clear()               { LinkedHashMap.this.clear(); }
    //返回迭代键值对的迭代器
    public final Iterator<Map.Entry<K,V>> iterator() {
        return new LinkedEntryIterator();
    }
    //键-值对集合是否包含指定的对象
    public final boolean contains(Object o) {
        if (!(o instanceof Map.Entry))
            return false;
        Map.Entry<?,?> e = (Map.Entry<?,?>) o;
        Object key = e.getKey();
        Node<K,V> candidate = getNode(hash(key), key);
        return candidate != null && candidate.equals(e);
    }
    //从映射集中移除指定的集合
    public final boolean remove(Object o) {
        if (o instanceof Map.Entry) {
            Map.Entry<?,?> e = (Map.Entry<?,?>) o;
            Object key = e.getKey();
            Object value = e.getValue();
            return removeNode(hash(key), key, value, true, true) != null;
        }
        return false;
    }
    public final Spliterator<Map.Entry<K,V>> spliterator() {
        return Spliterators.spliterator(this, Spliterator.SIZED |
                                        Spliterator.ORDERED |
                                        Spliterator.DISTINCT);
    }
    //对键-值对集合中所有键-值对执行指定的操作action
    public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
        if (action == null)
            throw new NullPointerException();
        int mc = modCount;
        for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
            action.accept(e);
        //检查并发修改情况
        if (modCount != mc)
            throw new ConcurrentModificationException();
    }
}

// Map overrides
//对这个Map中所有的键和值执行action.
public void forEach(BiConsumer<? super K, ? super V> action) {
    if (action == null)
        throw new NullPointerException();
    int mc = modCount;
    //遍历链表,对节点指定action操作
    for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
        action.accept(e.key, e.value);
    //检查并发修改情况
    if (modCount != mc)
        throw new ConcurrentModificationException();
}

//用键-值对在给指定函数上的执行结果替换键-值对的值.
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
    if (function == null)
        throw new NullPointerException();
    int mc = modCount;
    //遍历链表,根据键和值计算得到的函数值替换原先键-值映射的值
    for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
        e.value = function.apply(e.key, e.value);
    //检查并发修改情况
    if (modCount != mc)
        throw new ConcurrentModificationException();
}

// Iterators
//用于迭代LinkedHashMap的内部类
abstract class LinkedHashIterator {
  //双向链表的想下一个节点
    LinkedHashMap.Entry<K,V> next;
    //当前节点
    LinkedHashMap.Entry<K,V> current;
    int expectedModCount;

    LinkedHashIterator() {
        next = head;
        //用于fail-fast机制
        expectedModCount = modCount;
        current = null;
    }
    
    //是否存在下一个元素
    public final boolean hasNext() {
        return next != null;
    }
    
    //返回下一个节点
    final LinkedHashMap.Entry<K,V> nextNode() {
        LinkedHashMap.Entry<K,V> e = next;
        //检查并发修改情况
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        if (e == null)
            throw new NoSuchElementException();
        current = e;
        next = e.after;
        return e;
    }
    //移除迭代的当前节点
    public final void remove() {
        Node<K,V> p = current;
        if (p == null)
            throw new IllegalStateException();
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        current = null;
        K key = p.key;
        removeNode(hash(key), key, null, false, false);
        expectedModCount = modCount;
    }
}

final class LinkedKeyIterator extends LinkedHashIterator
    implements Iterator<K> {
    public final K next() { return nextNode().getKey(); }
}

final class LinkedValueIterator extends LinkedHashIterator
    implements Iterator<V> {
    public final V next() { return nextNode().value; }
}

final class LinkedEntryIterator extends LinkedHashIterator
    implements Iterator<Map.Entry<K,V>> {
    public final Map.Entry<K,V> next() { return nextNode(); }
}

遍历方法

1.遍历LinkedHashMap的键-值对。

通过调用entrySet()方法获取LinkedHashMap的键-值对映射集合Set,然后迭代Set集合获取key和value。

2.遍历LinkedHashMap的键。

调用keySet()方法获取LinkedHashMap的键集合Set,然后迭代Set集合获取键,通过调用get()方法获取指定键对应的值。

3.遍历LinkedHashMap的值

调用values()方法获取LinkedHashMap的值集合Collection,迭代获取所有值。

需要注意的是前两种方式不管是遍历LinkedHashMap的键-值对还是键集,可以通过对应的方法获取所有的键和值。最后一种只能遍历LinkedHashMap的值,无法获取键值。
 

public class AIteratorLinkedHashMap{
  public static void main(String[] args) {
    Map<String,Integer> map = new LinkedHashMap<>();
    map.put("first", 1);
    map.put("second",2);
    map.put("third", 3);
    map.put("forth", 4);
    iteratorLinkedHashMapByEntrySet(map);
    iteratorLinkedHashMapByKeySet(map);
    iteratorLinkedHashMapByValueSet(map);
  }
  
  //第一种方式迭代键-值对
  private static void iteratorLinkedHashMapByEntrySet(Map<String,Integer> map) {
    Set<Entry<String, Integer>> entrySet = map.entrySet();
    //迭代器
    Iterator<Entry<String, Integer>> iterator = entrySet.iterator();
    while(iterator.hasNext()) {
      Entry<String, Integer> entry = iterator.next();
      System.out.println(entry.getKey()+"------"+entry.getValue());
    }
    //foreach循环
    for(Entry<String,Integer> entry:entrySet) {
      System.out.println(entry.getKey()+"------"+entry.getValue());
    }
  }
  
  //第二种方式迭代键集合
  private static void iteratorLinkdHashMapByKeySet(Map<String,Integer> map) {
    Set<String> keySet = map.keySet();
    //迭代器
    Iterator<String> iterator = keySet.iterator();
    while(iterator.hasNext()) {
      String key = iterator.next();
      System.out.println(key+"-------"+map.get(key));
    }
    //foreach循环
    for(String key:keySet) {
      System.out.println(key+"---------"+map.get(key));
    }
  }
  //第三种方式迭代值
  private static void iteratorLinkedHashMapByValueSet(Map<String,Integer> map) {
    Collection<Integer> values = map.values();
    //迭代器
    Iterator<Integer> iterator = values.iterator();
    while(iterator.hasNext()) {
      System.out.println(iterator.next());
    }
    //forearch循环
    for(Integer in:values) {
      System.out.println(in);
    }
  }
}

案例

    案例基于LinkedHashMap,重写了removeEldestEntry()方法实现了一个简单的LRU缓存。策略主要是判断是缓存中节点是否超过指定的数量,超过阈值将会清缓存处理。

public class LRUCache<K,V> extends LinkedHashMap<K,V> {
  
  private int maxSize;
  private static final int DEFAULTMAXSIZE= 50;
  
  public LRUCache() {
    this(DEFAULTMAXSIZE);
  }
  
  public LRUCache(int size) {
    super(size,0.75f,true);
    this.maxSize = size;
  }
  
  public void save(K key,V value) {
    put(key,value);
  }
  
  public V getValue(K key) {
    return get(key);
  }
  
  public boolean isExistKey(K key) {
    return containsKey(key);
  }
 
  @Override 
  public boolean removeEldestEntry(Map.Entry<K, V> eldest) {
    return size()>maxSize;
  }
  
  public static void main(String[] args) {
    LRUCache<Integer,String> cache = new LRUCache<>(3);
    for(int i = 0;i<5;i++) {
      cache.put(i, String.valueOf(i));
    }
    System.out.println(cache);
    
    System.out.println("访问键为2节点:"+cache.getValue(2));
    System.out.println("访问键为2节点后缓存的变化---"+cache);
    
    cache.put(6, String.valueOf(6));
    System.out.println("向缓存中添加节点6后---"+cache);
  }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值