Java集合类:LinkedHashMap

LinkedHashMap源码到面试题的全解析 是Java提供的一个集合类,它继承自HashMap,并在HashMap支持遍历时会按照插入顺序有序进行迭代。支持按照元素访问顺序排序,适用于封装LRU缓存工具。因为内部使用双向链表维护各个节点,所以遍历时的效率和元素个数成正比,相较于和容量成正比的HashMap来说,迭代效率会高很多。逻辑结构如下图所示,它是在HashMap基础上在各个节点之间维护一条双向链表,使得原本散列在不同bucket上的节点、链表、红黑树有序关联起来。是Java集合框架中HashMap的一个子类,它继承了HashMap。 阅读详情

一、初识LinkedHashMap

HashMap是一种非常常见、非常有用的集合,但在多线程情况下使用不当会有线程安全问题。

大多数情况下,只要不涉及线程安全问题,Map基本都可以使用HashMap,不过HashMap有一个问题,就是迭代HashMap的顺序并不是HashMap放置的顺序,也就是无序。HashMap的这一缺点往往会带来困扰,因为有些场景,我们期待一个有序的Map。

这个时候,LinkedHashMap就闪亮登场了,它虽然增加了时间和空间上的开销,但是通过维护一个运行于所有条目的双向链表,LinkedHashMap保证了元素迭代的顺序。该迭代顺序可以是插入顺序或者是访问顺序。

二、四个关注点在LinkedHashMap上的答案

关  注  点结      论
LinkedHashMap是否允许空Key和Value都允许空
LinkedHashMap是否允许重复数据Key重复会覆盖、Value允许重复
LinkedHashMap是否有序有序
LinkedHashMap是否线程安全非线程安全

 

三、LinkedHashMap基本结构

关于LinkedHashMap,先提两点:

1、LinkedHashMap可以认为是HashMap+LinkedList,即它既使用HashMap操作数据结构,又使用LinkedList维护插入元素的先后顺序。

2、LinkedHashMap的基本实现思想就是----多态。可以说,理解多态,再去理解LinkedHashMap原理会事半功倍;反之也是,对于LinkedHashMap原理的学习,也可以促进和加深对于多态的理解。

为什么可以这么说,首先看一下,LinkedHashMap的定义: 

public class LinkedHashMap<K,V>
    extends HashMap<K,V>
    implements Map<K,V>
{
    ...
}

看到,LinkedHashMap是HashMap的子类,自然LinkedHashMap也就继承了HashMap中所有非private的方法。再看一下LinkedHashMap中本身的方法:

看到LinkedHashMap中并没有什么操作数据结构的方法,也就是说LinkedHashMap操作数据结构(比如put一个数据),和HashMap操作数据的方法完全一样,无非就是细节上有一些的不同罢了。

LinkedHashMap只定义了两个属性:

复制代码

/**
 * The head of the doubly linked list.
 * 双向链表的头节点
 */
private transient Entry<K,V> header;
/**
 * The iteration ordering method for this linked hash map: true
 * for access-order, false for insertion-order.
 * true表示最近最少使用次序,false表示插入顺序
 */
private final boolean accessOrder;

复制代码

LinkedHashMap一共提供了五个构造方法:

复制代码

// 构造方法1,构造一个指定初始容量和负载因子的、按照插入顺序的LinkedList
public LinkedHashMap(int initialCapacity, float loadFactor) {
    super(initialCapacity, loadFactor);
    accessOrder = false;
}
// 构造方法2,构造一个指定初始容量的LinkedHashMap,取得键值对的顺序是插入顺序
public LinkedHashMap(int initialCapacity) {
    super(initialCapacity);
    accessOrder = false;
}
// 构造方法3,用默认的初始化容量和负载因子创建一个LinkedHashMap,取得键值对的顺序是插入顺序
public LinkedHashMap() {
    super();
    accessOrder = false;
}
// 构造方法4,通过传入的map创建一个LinkedHashMap,容量为默认容量(16)和(map.zise()/DEFAULT_LOAD_FACTORY)+1的较大者,装载因子为默认值
public LinkedHashMap(Map<? extends K, ? extends V> m) {
    super(m);
    accessOrder = false;
}
// 构造方法5,根据指定容量、装载因子和键值对保持顺序创建一个LinkedHashMap
public LinkedHashMap(int initialCapacity,
             float loadFactor,
                         boolean accessOrder) {
    super(initialCapacity, loadFactor);
    this.accessOrder = accessOrder;
}

复制代码

从构造方法中可以看出,默认都采用插入顺序来维持取出键值对的次序。所有构造方法都是通过调用父类的构造方法来创建对象的。

LinkedHashMap和HashMap的区别在于它们的基本数据结构上,看一下LinkedHashMap的基本数据结构,也就是Entry:

复制代码

private static class Entry<K,V> extends HashMap.Entry<K,V> {
    // These fields comprise the doubly linked list used for iteration.
    Entry<K,V> before, after;

    Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
        super(hash, key, value, next);
    }
    ...
}

复制代码

列一下Entry里面有的一些属性吧:

1、K key

2、V value

3、Entry<K, V> next

4、int hash

5、Entry<K, V> before

6、Entry<K, V> after

其中前面四个,也就是红色部分是从HashMap.Entry中继承过来的;后面两个,也就是蓝色部分是LinkedHashMap独有的。不要搞错了next和before、After,next是用于维护HashMap指定table位置上连接的Entry的顺序的,before、After是用于维护Entry插入的先后顺序的。

还是用图表示一下,列一下属性而已:

第一张图为LinkedHashMap整体结构图,第二张图专门把循环双向链表抽取出来,直观一点,注意该循环双向链表的头部存放的是最久访问的节点或最先插入的节点,尾部为最近访问的或最近插入的节点,迭代器遍历方向是从链表的头部开始到链表尾部结束,在链表尾部有一个空的header节点,该节点不存放key-value内容,为LinkedHashMap类的成员属性,循环双向链表的入口。

四、初始化LinkedHashMap

假如有这么一段代码:

复制代码

1 public static void main(String[] args)
2 {
3     LinkedHashMap<String, String> linkedHashMap =
4             new LinkedHashMap<String, String>();
5     linkedHashMap.put("111", "111");
6     linkedHashMap.put("222", "222");
7 }

复制代码

首先是第3行~第4行,new一个LinkedHashMap出来,看一下做了什么:

通过源代码可以看出,在LinkedHashMap的构造方法中,实际调用了父类HashMap的相关构造方法来构造一个底层存放的table数组。

1 public LinkedHashMap() {
2     super();
3     accessOrder = false;
4 }
1 public HashMap() {
2     this.loadFactor = DEFAULT_LOAD_FACTOR;
3     threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
4     table = new Entry[DEFAULT_INITIAL_CAPACITY];
5     init();
6 }

我们已经知道LinkedHashMap的Entry元素继承HashMap的Entry,提供了双向链表的功能。在上述HashMap的构造器中,最后会调用init()方法,进行相关的初始化,这个方法在HashMap的实现中并无意义,只是提供给子类实现相关的初始化调用。
LinkedHashMap重写了init()方法,在调用父类的构造方法完成构造后,进一步实现了对其元素Entry的初始化操作。

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

这里出现了第一个多态:init()方法。尽管init()方法定义在HashMap中,但是由于:

1、LinkedHashMap重写了init方法

2、实例化出来的是LinkedHashMap

因此实际调用的init方法是LinkedHashMap重写的init方法。假设header的地址是0x00000000,那么初始化完毕,实际上是这样的:

注意这个header,hash值为-1,其他都为null,也就是说这个header不放在数组中,就是用来指示开始元素和标志结束元素的。

header的目的是为了记录第一个插入的元素是谁,在遍历的时候能够找到第一个元素。

五、LinkedHashMap存储元素

LinkedHashMap并未重写父类HashMap的put方法,而是重写了父类HashMap的put方法调用的子方法void recordAccess(HashMap m)  ,void addEntry(int hash, K key, V value, int bucketIndex) 和void createEntry(int hash, K key, V value, int bucketIndex),提供了自己特有的双向链接列表的实现。

继续看LinkedHashMap存储元素,也就是put("111","111")做了什么,首先当然是调用HashMap的put方法:

复制代码

 1 //这个方法应该挺熟悉的,如果看了HashMap的解析的话
 2 public V put(K key, V value) {
 3     //key为null的情况
 4     if (key == null)
 5         return putForNullKey(value);
 6     //通过key算hash,进而算出在数组中的位置,也就是在第几个桶中
 7     int hash = hash(key.hashCode());
 8     int i = indexFor(hash, table.length);
 9     //查看桶中是否有相同的key值,如果有就直接用新值替换旧值,而不用再创建新的entry了
10     for (Entry<K,V> e = table[i]; e != null; e = e.next) {
11         Object k;
12         if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
13             V oldValue = e.value;
14             e.value = value;
15             e.recordAccess(this);
16             return oldValue;
17         }
18     }
19 
20     modCount++;    
21     //上面度是熟悉的东西,最重要的地方来了,就是这个方法,LinkedHashMap执行到这里,addEntry()方法不会执行HashMap中的方法,
22     //而是执行自己类中的addEntry方法,
23     addEntry(hash, key, value, i);
24     return null;
25 }

复制代码

第23行又是一个多态,因为LinkedHashMap重写了addEntry方法,因此addEntry调用的是LinkedHashMap重写了的方法:

复制代码

 1 void addEntry(int hash, K key, V value, int bucketIndex) {
 2     //调用create方法,将新元素以双向链表的的形式加入到映射中
 3     createEntry(hash, key, value, bucketIndex);
 4 
 5     // Remove eldest entry if instructed, else grow capacity if appropriate
 6     // 删除最近最少使用元素的策略定义  
 7     Entry<K,V> eldest = header.after;
 8     if (removeEldestEntry(eldest)) {
 9         removeEntryForKey(eldest.key);
10     } else {
11         if (size >= threshold)
12             resize(2 * table.length);
13     }
14 }

复制代码

因为LinkedHashMap由于其本身维护了插入的先后顺序,因此LinkedHashMap可以用来做缓存,第7行~第9行是用来支持FIFO算法的,这里暂时不用去关心它。看一下createEntry方法: 

复制代码

1 void createEntry(int hash, K key, V value, int bucketIndex) {
2     HashMap.Entry<K,V> old = table[bucketIndex];
3     Entry<K,V> e = new Entry<K,V>(hash, key, value, old);
4     table[bucketIndex] = e;
5     //将该节点插入到链表尾部
6     e.addBefore(header);
7     size++;
8 }

复制代码

private void addBefore(Entry<K,V> existingEntry) {
    after  = existingEntry;
    before = existingEntry.before;
    before.after = this;
    after.before = this;
}

createEntry(int hash,K key,V value,int bucketIndex)方法覆盖了父类HashMap中的方法。这个方法不会拓展table数组的大小。该方法首先保留table中bucketIndex处的节点,然后调用Entry的构造方法(将调用到父类HashMap.Entry的构造方法)添加一个节点,即将当前节点的next引用指向table[bucketIndex] 的节点,之后调用的e.addBefore(header)是修改链表,将e节点添加到header节点之前。

第2行~第4行的代码和HashMap没有什么不同,新添加的元素放在table[i]上,差别在于LinkedHashMap还做了addBefore操作,这四行代码的意思就是让新的Entry和原链表生成一个双向链表。假设字符串111放在位置table[1]上,生成的Entry地址为0x00000001,那么用图表示是这样的:

如果熟悉LinkedList的源码应该不难理解,还是解释一下,注意下existingEntry表示的是header:

1、after=existingEntry,即新增的Entry的after=header地址,即after=0x00000000

2、before=existingEntry.before,即新增的Entry的before是header的before的地址,header的before此时是0x00000000,因此新增的Entry的before=0x00000000

3、before.after=this,新增的Entry的before此时为0x00000000即header,header的after=this,即header的after=0x00000001

4、after.before=this,新增的Entry的after此时为0x00000000即header,header的before=this,即header的before=0x00000001

这样,header与新增的Entry的一个双向链表就形成了。再看,新增了字符串222之后是什么样的,假设新增的Entry的地址为0x00000002,生成到table[2]上,用图表示是这样的:

 

就不细解释了,只要before、after清除地知道代表的是哪个Entry的就不会有什么问题。

注意,这里的插入有两重含义:

1.从table的角度看,新的entry需要插入到对应的bucket里,当有哈希冲突时,采用头插法将新的entry插入到冲突链表的头部。
2.从header的角度看,新的entry需要插入到双向链表的尾部。

总得来看,再说明一遍,LinkedHashMap的实现就是HashMap+LinkedList的实现方式,以HashMap维护数据结构,以LinkList的方式维护数据插入顺序。

六、LinkedHashMap读取元素

LinkedHashMap重写了父类HashMap的get方法,实际在调用父类getEntry()方法取得查找的元素后,再判断当排序模式accessOrder为true时(即按访问顺序排序),先将当前节点从链表中移除,然后再将当前节点插入到链表尾部。由于的链表的增加、删除操作是常量级的,故并不会带来性能的损失。

复制代码

/**
 * 通过key获取value,与HashMap的区别是:当LinkedHashMap按访问顺序排序的时候,会将访问的当前节点移到链表尾部(头结点的前一个节点)
 */
public V get(Object key) {
    // 调用父类HashMap的getEntry()方法,取得要查找的元素。  
    Entry<K,V> e = (Entry<K,V>)getEntry(key);
    if (e == null)
        return null;
    // 记录访问顺序。
    e.recordAccess(this);
    return e.value;
}

复制代码

复制代码

/**
 * 在HashMap的put和get方法中,会调用该方法,在HashMap中该方法为空
 * 在LinkedHashMap中,当按访问顺序排序时,该方法会将当前节点插入到链表尾部(头结点的前一个节点),否则不做任何事
 */
void recordAccess(HashMap<K,V> m) {
    LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
    //当LinkedHashMap按访问排序时
    if (lm.accessOrder) {
        lm.modCount++;
        //移除当前节点
        remove();
        //将当前节点插入到头结点前面
        addBefore(lm.header);
    }
}

复制代码

复制代码

/**
 * 移除节点,并修改前后引用
 */
private void remove() {
    before.after = after;
    after.before = before;
}

复制代码

private void addBefore(Entry<K,V> existingEntry) {
    after  = existingEntry;
    before = existingEntry.before;
    before.after = this;
    after.before = this;
}

七、利用LinkedHashMap实现LRU算法缓存

前面讲了LinkedHashMap添加元素,删除、修改元素就不说了,比较简单,和HashMap+LinkedList的删除、修改元素大同小异,下面讲一个新的内容。

LinkedHashMap可以用来作缓存,比方说LRUCache,看一下这个类的代码,很简单,就十几行而已:

复制代码

public class LRUCache extends LinkedHashMap
{
    public LRUCache(int maxSize)
    {
        super(maxSize, 0.75F, true);
        maxElements = maxSize;
    }

    protected boolean removeEldestEntry(java.util.Map.Entry eldest)
    {
        return size() > maxElements;
    }

    private static final long serialVersionUID = 1L;
    protected int maxElements;
}

复制代码

顾名思义,LRUCache就是基于LRU算法的Cache(缓存),这个类继承自LinkedHashMap,而类中看到没有什么特别的方法,这说明LRUCache实现缓存LRU功能都是源自LinkedHashMap的。LinkedHashMap可以实现LRU算法的缓存基于两点:

1、LinkedList首先它是一个Map,Map是基于K-V的,和缓存一致

2、LinkedList提供了一个boolean值可以让用户指定是否实现LRU

那么,首先我们了解一下什么是LRU:LRU即Least Recently Used,最近最少使用,也就是说,当缓存满了,会优先淘汰那些最近最不常访问的数据。比方说数据a,1天前访问了;数据b,2天前访问了,缓存满了,优先会淘汰数据b。

我们看一下LinkedList带boolean型参数的构造方法:

public LinkedHashMap(int initialCapacity,
         float loadFactor,
                     boolean accessOrder) {
    super(initialCapacity, loadFactor);
    this.accessOrder = accessOrder;
}

就是这个accessOrder,它表示:

(1)false,所有的Entry按照插入的顺序排列

(2)true,所有的Entry按照访问的顺序排列

第二点的意思就是,如果有1 2 3这3个Entry,那么访问了1,就把1移到尾部去,即2 3 1。每次访问都把访问的那个数据移到双向队列的尾部去,那么每次要淘汰数据的时候,双向队列最头的那个数据不就是最不常访问的那个数据了吗?换句话说,双向链表最头的那个数据就是要淘汰的数据。

"访问",这个词有两层意思:

1、根据Key拿到Value,也就是get方法

2、修改Key对应的Value,也就是put方法

首先看一下get方法,它在LinkedHashMap中被重写:

复制代码

public V get(Object key) {
    Entry<K,V> e = (Entry<K,V>)getEntry(key);
    if (e == null)
        return null;
    e.recordAccess(this);
    return e.value;
} 

复制代码

然后是put方法,沿用父类HashMap的:

复制代码

 1 public V put(K key, V value) {
 2     if (key == null)
 3         return putForNullKey(value);
 4     int hash = hash(key.hashCode());
 5     int i = indexFor(hash, table.length);
 6     for (Entry<K,V> e = table[i]; e != null; e = e.next) {
 7         Object k;
 8         if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
 9             V oldValue = e.value;
10             e.value = value;
11             e.recordAccess(this);
12             return oldValue;
13         }
14     }
15 
16     modCount++;
17     addEntry(hash, key, value, i);
18     return null;
19 }

复制代码

修改数据也就是第6行~第14行的代码。看到两端代码都有一个共同点:都调用了recordAccess方法,且这个方法是Entry中的方法,也就是说每次的recordAccess操作的都是某一个固定的Entry。

recordAccess,顾名思义,记录访问,也就是说你这次访问了双向链表,我就把你记录下来,怎么记录?把你访问的Entry移到尾部去。这个方法在HashMap中是一个空方法,就是用来给子类记录访问用的,看一下LinkedHashMap中的实现:

复制代码

void recordAccess(HashMap<K,V> m) {
    LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
    if (lm.accessOrder) {
        lm.modCount++;
        remove();
        addBefore(lm.header);
    }
}

复制代码

private void remove() {
    before.after = after;
    after.before = before;
}
private void addBefore(Entry<K,V> existingEntry) {
    after  = existingEntry;
    before = existingEntry.before;
    before.after = this;
    after.before = this;
}

看到每次recordAccess的时候做了两件事情:

1、把待移动的Entry的前后Entry相连

2、把待移动的Entry移动到尾部

当然,这一切都是基于accessOrder=true的情况下。最后用一张图表示一下整个recordAccess的过程吧:

void recordAccess(HashMap<K,V> m) 这个方法就是我们一开始说的,accessOrder为true时,就是使用的访问顺序,访问次数最少到访问次数最多,此时要做特殊处理。处理机制就是访问了一次,就将自己往后移一位,这里就是先将自己删除了,然后在把自己添加,这样,近期访问的少的就在链表的开始,最近访问的元素就会在链表的末尾。如果为false。那么默认就是插入顺序,直接通过链表的特点就能依次找到插入元素,不用做特殊处理。

八、代码演示LinkedHashMap按照访问顺序排序的效果

最后代码演示一下LinkedList按照访问顺序排序的效果,验证一下上一部分LinkedHashMap的LRU功能:

复制代码

public static void main(String[] args)
{
    LinkedHashMap<String, String> linkedHashMap =
            new LinkedHashMap<String, String>(16, 0.75f, true);
    linkedHashMap.put("111", "111");
    linkedHashMap.put("222", "222");
    linkedHashMap.put("333", "333");
    linkedHashMap.put("444", "444");
    loopLinkedHashMap(linkedHashMap);
    linkedHashMap.get("111");
    loopLinkedHashMap(linkedHashMap);
    linkedHashMap.put("222", "2222");
    loopLinkedHashMap(linkedHashMap);
}
    
public static void loopLinkedHashMap(LinkedHashMap<String, String> linkedHashMap)
{
    Set<Map.Entry<String, String>> set = inkedHashMap.entrySet();
    Iterator<Map.Entry<String, String>> iterator = set.iterator();
    
    while (iterator.hasNext())
    {
        System.out.print(iterator.next() + "\t");
    }
    System.out.println();
}

复制代码

注意这里的构造方法要用三个参数那个且最后的要传入true,这样才表示按照访问顺序排序。看一下代码运行结果:

111=111    222=222    333=333    444=444    
222=222    333=333    444=444    111=111    
333=333    444=444    111=111    222=2222   

代码运行结果证明了两点:

1、LinkedList是有序的

2、每次访问一个元素(get或put),被访问的元素都被提到最后面去了。

Map集合之LinkedHashmap LinkedHashmap源码剖析 LinkedHashMap简介 LinkedHashMap 是 HashMap 的子类,与 HashMap 有着同样的存储结构,但它加入了一个双向链表的头结点,将所有put 到 LinkedHashmap 的节点一一串成了一个双向循环链表,因此它保留了节点插入的顺序,可以使节点的输出顺序与输入顺序相同 LinkedHashMap 可以用来实现 LRU 算法 LinkedHashMap 同样是非线程安全的,只在单线程环境下使用 LinkedHashMap源码 publi 阅读详情

相关推荐

多线程下安全的集合类

ConcurrentHashMap 和 java.util.HashTable 类很相似,但 ConcurrentHashMap 能够提供比 HashTable 更好的并发性能。Java 5 添加了一个新的包到 Java 平台,java.util.concurrent 包。java.util.concurrent.ConcurrentMap 接口表示了一个能够对别人的访问(插入和提取)进行并发处理的 java.util.Map。4、Stack: 栈,也是线程安全的,继承于Vector。

且将&的博客 1146

Java基础 LinkedHashMap

LinkedHashMap是HashMap和双向链表的合二为一,即一个将所有Entry节点链入一个双向链表的HashMap(LinkedHashMap = HashMap + 双向链表)LinkedHashMap和HashMap是Java Collection Framework 的重要成员,也是Map族(如下图所示)LinkedHashMap是HashMap的子类(拥有HashMap的所有特性)

深入Java世界:探索编程之美与技术深度 1718

全面了解Java中常用的集合类:LinkedHashMap的应用与实践

Java是一种面向对象的编程语言,集合类是在Java编程中常用的一种数据结构,包括数组和集合。在Java中,集合类可以用来存储和操作一组对象数据,是Java编程中不可或缺的一部分。在本文中,我们将详细介绍Java中一个常用的集合类:LinkedHashMap。本文将重点介绍LinkedHashMap这一集合类的应用和实践。我们将从源代码解析开始,详细介绍LinkedHashMap的实现方式和内部结构。接着,我们将通过实际应用场景案例和优缺点分析,深入剖析LinkedHashMap的特点和适用范围。

**My Coding Family** 1362

LinkedHashMap详解

一、概念及概述 LinkedHashMap 继承自 HashMap,在 HashMap 基础上,通过维护一条双向链表,解决了 HashMap 不能随时保持遍历顺序和插入顺序一致的问题。在一些场景下,该特性很有用,比如缓存。在实现上,LinkedHashMap 很多方法直接继承自 HashMap,仅为维护双向链表覆写了部分方法。 当我们希望有顺序地去存储key-value时,就需要使用Linke...

pengjiawei@的专栏 2万+

滚雪球学Java(68):全面了解Java中常用的集合类:LinkedHashMap的应用与实践

🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,助你一臂之力,带你早日登顶🚀,欢迎大家关注&&收藏!持续更新中,up!up!up!!

**My Coding Family** 1220

AutoSAR实战:手把手教你配置PNC(Partial Network Cluster)实现精准休眠与唤醒

本文详细介绍了如何在AutoSAR中配置PNC(Partial Network Cluster)实现精准休眠与唤醒,通过DaVinci Configurator工具逐步指导,结合ComM模块的协同调试,优化车载电子系统的能耗管理。文章包含实战步骤、常见错误排查及功耗优化数据,适合汽车电子开发工程师参考。

weixin_33725239的博客 466

Java集合类源码分析:LinkedHashMap如何维护插入顺序

继承自HashMap,在保留HashMap所有特性的基础上,通过维护一个双向链表来记录元素的插入顺序或访问顺序。这使得它既具有HashMap的O(1)时间复杂度的查询性能,又能提供可预测的迭代顺序。java// 基本使用示例// 迭代顺序保证为A->B->C通过巧妙的双向链表设计,在继承HashMap高效查找的基础上,完美解决了哈希表无序的问题。通过扩展节点类添加前后指针插入时自动维护链表顺序支持可选的访问顺序模式迭代器基于链表实现保证顺序通过组合而非修改来扩展功能。

2501_93833736的博客 1785

C#实战:如何用winmm.dll打造毫秒级高精度定时器(附完整代码)

本文详细介绍了在C#中利用winmm.dll实现毫秒级高精度定时器的完整方案。针对.NET内置定时器精度不足的问题,通过调用timeSetEvent等原生API,提供了超越系统时钟中断周期的精准定时能力。文章包含核心原理剖析、完整的封装类代码以及音视频处理、游戏开发等场景下的实战应用与性能调优指南。

ujm5678901的博客 690

Java集合类源码解析:LinkedHashMap

前言 今天继续学习关于Map家族的另一个类 LinkedHashMap 。先说明一下,LinkedHashMap 是继承于 HashMap 的,所以本文只针对 LinkedHashMap 的特性学习,跟HashMap 相关的一些特性就不做进一步的解析了,大家有疑惑的可以看之前的博文。 深入解析 LinkedHashMap的基本结构 首先,看一下LinkedHashMap类的定义结构: p...

weixin_30413739的博客 78

实战测评:4大搜索API(You.com/Tavily/Exa/Perplexity)谁更适合你的AI项目?附Python调用代码

本文实战测评了You.com、Tavily、Exa和Perplexity四大搜索API在AI项目中的集成应用,详细对比了它们的技术特性和Python调用方法。针对LLM实时数据需求,You.com的优化设计表现突出,支持32k大上下文窗口和快速响应。文章提供了完整的代码示例和场景化选型建议,帮助开发者高效集成搜索API到AI项目中。

weixin_42569329的博客 482

从“看不懂Sansen”到亲手搭建:一个Class AB输出级+浮动电压源的保姆级设计复盘

本文详细记录了从理论困惑到实践突破的Class AB输出级与浮动电压源设计全过程。通过电流镜调试、恒定跨导优化及版图匹配等关键步骤,作者实现了高性能运放设计,并分享了从仿真到实测的宝贵经验,为模拟电路初学者提供实用指导。

weixin_33737774的博客 869

Java集合类源码分析(七):Map接口之LinkedHashMap实现类

Java集合类源码分析(七):Map接口之LinkedHashMap实现类

MyKelly_2013 263

FPGA新手必看:XC7Z020最小系统搭建全攻略(附原理图解析)

本文为FPGA新手提供了XC7Z020最小系统搭建的完整实战指南。文章详细解析了电源架构、时钟复位、配置调试等关键硬件电路设计,并重点剖析了外围电路与端口引脚的连接要点及常见陷阱,帮助读者从零开始构建稳定可靠的开发平台。

weixin_29189377的博客 360

【Java基础】详解Java中Map集合类 HashMap、Hashtable、LinkedHashMap、TreeMap(不涉及底层实现)

详解Java中Map集合类

笔记 322

Qualcomm平台下基于QSEE的指纹识别方案移植之四TA移植

本文只介绍TA移植的过程,不做技术分析,关于TA开发另做记录。 一、TA介绍 TA,Trusted application 可信应用,是运行于QSEE中的可执行程序。 指纹TA文件如下,形式一般是 xxx.mbn; xxx.b0 ~ xxx.b6, xxx.flist, xxx.mdt; xxx.mbn 可以理解为其它几个文件的集合,签名的时候用的就是这个文件,签完名之后,有对应的工具将它...

梦路遥 7549

【推荐系统】推荐算法:冷启动-召回-粗排-精排-重排 解读

【推荐系统】推荐算法:冷启动-召回-粗排-精排-重排 解读

Danger的博客 3761

Java中常见的集合类及其特点解读

Java中常见的集合类及其特点解读

qq_44577699的博客 1017

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

java中HashMap,LinkedHashMap,TreeMap,HashTable的区别

java中HashMap,LinkedHashMap,TreeMap,HashTable的区别   java中HashMap,LinkedHashMap,TreeMap,HashTable的区别 java为数据结构中的映射定义了一个接口java.util.Map;它有四个实现类,分别是HashMap Hashtable LinkedHashMap 和TreeMap Map主要用于存储

夜深人静后的码农 769

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

转:LinkedHashMap和HashMap的比较使用

importjava.util.HashMap; importjava.util.Iterator; importjava.util.LinkedHashMap; importjava.util.Map; publicclassTestLinkedHashMap { publicstaticvoidmain(String args[]) ...

dianku0852的博客 130

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

LinkedHashMap和TreeMap在遍历数据时的区别

话不多说,直接上代码: 示例: LinkedHashMap输出结果: TreeMap输出结果: 由上述代码及结果可知: LinkedHashMap是根据插入数据的顺序遍历 TreeMap是根据1.2.3....的顺序遍历出结果 ...

cccooode的博客 725

对 Map HashMap LinkedHashMap TreeMap 的深入认识

HashMap,LinkedHashMap,TreeMap都属于Map。 Map 主要用于存储键(key)值(value)对,根据键得到值,因此键不允许键重复,但允许值重复。 HashMap 是一个最常用的Map,它根据键的 HashCode 值存储数据,根据键可以直接获取它的值,具有很快的访问速度。HashMap最多只允许一条记录的键为Null;允许多条记录的值为 Null;Hash...

toulezu的专栏 185

java主要集合类的数据结构

一. List  1).ArrayList     ArrayList维护着一个对象数组。如果调用new ArrayList()后,它会默认初始一个size=10的数组。   每次add操作都要检查数组容量,如果不够,重新设置一个初始容量1.5倍大小的新数组,然后再把每个元素copy过去。   在数组中间插入或删除,都要移动后面的所有元素。(使用System.arraycopy

Sup_Heaven的专栏 1910
上一篇: java的内存模型
下一篇: 限流-------------令牌桶算法
U & Me
博客等级 码龄9年 3粉丝 10原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值