map和linkedhashmap,linkedhashmap构造方法
00-1010 linkedhashmapget()put()remove()linkedhashTheLinkedHashMap的经典用法概述
00-1010如果你已经看过前面关于HashSet和HashMap,以及TreeSet和TreeMap的解释,你一定可以认为本文将要讲解的LinkedHashSet和LinkedHashMap其实是一回事。LinkedHashSet和LinkedHashMap在Java中有相同的实现。前者只是包装了后者,也就是说LinkedHashSet中有一个LinkedHashMap(适配器模式)。因此,本文将重点分析LinkedHashMap。
LinkedHashMap实现了Map接口,该接口允许您放入具有空键的元素和插入具有空值的元素。从名字可以看出,这个容器是链表和HashMap的混合体,也就是说同时满足HashMap和链表的一些特性。LinkedHashMap可以看作是一个链表增强的HashMap。
实际上,LinkedHashMap是HashMap的直接子类。两者唯一的区别是LinkedHashMap在HashMap的基础上以双向链表的形式连接所有条目,从而保证元素的迭代顺序与插入顺序相同。上图是LinkedHashMap的结构图。主要部分与HashMap完全相同,只是header指向双向链表的头部(这是一个伪链表)。双向链表的迭代顺序是条目的插入顺序。
除了保持迭代日历的顺序,这种结构还有另一个优点。在迭代LinkedHashMap时,3360不需要像HashMap一样遍历整个表,只需要直接遍历表头指向的双向链表即可。也就是说,LinkedHashMap的迭代时间只与条目数有关,而与表的大小无关。
有两个参数会影响LinkedHashMap的性能:3360初始容量和负载系数。初始容量指定初始表的大小,负载因子用于指定自动扩展的临界值。当条目数量超过capacity*load_factor时,容器将自动扩展并再次散列。对于有许多插入元素的场景,将初始容量设置为较大可以减少重新散列的次数。
在将对象放入LinkedHashMap或LinkedHashSet时,有两种方法要特别注意: hashCode()和equals()。hashCode()方法决定了一个对象将被放在哪个桶中。当多个对象的哈希值冲突时,equals()方法确定这些对象是否是“同一个对象”。因此,如果要将自定义对象放入LinkedHashMap或LinkedHashSet,需要@Override hashCode()和equals()方法。
与源映射具有相同迭代顺序的链接hashMap:可以通过以下方式获得
void foo(Map m){ Map copy=new linked hashmap(m);}出于性能原因,LinkedHashMap不同步。如果需要在多线程环境下使用,需要程序员手动同步;或者用下面的方法把LinkedHashMap包装成一个同步的3360。
map m=collections . synchronized map(new linked hashmap(.));
目录
00-1010Get (objectkey)方法根据指定的键值返回相应的值。这个方法的流程与HashMap.get()方法的流程几乎完全相同。
总体介绍
放( K key, V value)方法是将指定的key, value
对添加到map
里。该方法首先会对map
做一次查找,看是否包含该元组,如果已经包含则直接返回,查找过程类似于get()
方法;如果没有找到,则会通过addEntry(int hash, K key, V value, int bucketIndex)
方法插入新的entry
。
注意,这里的插入有两重含义:
从table
的角度看,新的entry
需要插入到对应的bucket
里,当有哈希冲突时,采用头插法将新的entry
插入到冲突链表的头部。
从header
的角度看,新的entry
需要插入到双向链表的尾部。
addEntry()
源码如下:
// LinkedHashMap.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 = hash & (table.length-1);// hash%table.length } // 1.在冲突链表头部插入新的entry HashMap.Entry<K,V> old = table[bucketIndex]; Entry<K,V> e = new Entry<>(hash, key, value, old); table[bucketIndex] = e; // 2.在双向链表的尾部插入新的entry e.addBefore(header); size++;}上述代码中用到了
addBefore()
方法将新entry e
插入到双向链表头引用header
的前面,这样e
就成为双向链表中的最后一个元素。addBefore()
的源码如下:
// LinkedHashMap.Entry.addBefor(),将this插入到existingEntry的前面private void addBefore(Entry<K,V> existingEntry) { after = existingEntry; before = existingEntry.before; before.after = this; after.before = this;}上述代码只是简单修改相关
entry
的引用而已。
remove()
remove(Object key)
的作用是删除key
值对应的entry
,该方法的具体逻辑是在removeEntryForKey(Object key)
里实现的。 removeEntryForKey()
方法会首先找到key
值对应的entry
,然后删除该entry
(修改链表的相应引用)。查找过程跟get()
方法类似。
注意,这里的删除也有两重含义:
从table
的角度看,需要将该entry
从对应的bucket
里删除,如果对应的冲突链表不空,需要修改冲突链表的相应引用。
从header
的角度来看,需要将该entry
从双向链表中删除,同时修改链表中前面以及后面元素的相应引用。
removeEntryForKey()
对应的源码如下:
// LinkedHashMap.removeEntryForKey(),删除key值对应的entryfinal Entry<K,V> removeEntryForKey(Object key) {......int hash = (key == null) ? 0 : hash(key); int i = indexFor(hash, table.length);// hash&(table.length-1) Entry<K,V> prev = table[i];// 得到冲突链表 Entry<K,V> e = prev; while (e != null) {// 遍历冲突链表 Entry<K,V> next = e.next; Object k; if (e.hash == hash && ((k = e.key) == key (key != null && key.equals(k)))) {// 找到要删除的entry modCount++; size--; // 1. 将e从对应bucket的冲突链表中删除 if (prev == e) table[i] = next; else prev.next = next; // 2. 将e从双向链表中删除 e.before.after = e.after; e.after.before = e.before; return e; } prev = e; e = next; } return e;}
LinkedHashSet
前面已经说过LinkedHashSet是对LinkedHashMap的简单包装,对LinkedHashSet的函数调用都会转换成合适的LinkedHashMap方法,因此LinkedHashSet的实现非常简单,这里不再赘述。
public class LinkedHashSet<E> extends HashSet<E> implements Set<E>, Cloneable, java.io.Serializable { ...... // LinkedHashSet里面有一个LinkedHashMap public LinkedHashSet(int initialCapacity, float loadFactor) { map = new LinkedHashMap<>(initialCapacity, loadFactor); }...... public boolean add(E e) { //简单的方法转换 return map.put(e, PRESENT)==null; } ......}
LinkedHashMap经典用法
LinkedHashMap除了可以保证迭代顺序外c;还有一个非常有用的用法: 可以轻松实现一个采用了FIFO替换策略的缓存。具体说来,LinkedHashMap有一个子类方法
protected boolean removeEldestEntry(Map.Entry<K,V> eldest)该方法的作用是告诉Map是否要删除最老的Entry,所谓最老就是当前Map中最早插入的Entry,如果该方法返回
true
,最老的那个元素就会被删除。在每次插入新元素的之后LinkedHashMap会自动询问removeEldestEntry()是否要删除最老的元素。这样只需要在子类中重载该方法,当元素个数超过一定数量时让removeEldestEntry()返回true,就能够实现一个固定大小的FIFO策略的缓存。示例代码如下:
/** 一个固定大小的FIFO替换策略的缓存 */class FIFOCache<K, V> extends LinkedHashMap<K, V>{ private final int cacheSize; public FIFOCache(int cacheSize){ this.cacheSize = cacheSize; } // 当Entry个数超过cacheSize时,删除最老的Entry @Override protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { return size() > cacheSize; }}以上就是Map映射LinkedHashSet与LinkedHashMap示例解析的详细内容,更多关于Map映射LinkedHashSet与LinkedHashMap的资料请关注盛行IT其它相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。