【集合】LinkedHashMap如何保证有序

Java】——Map集合 java.util.Map<k,v>集合 Map集合的特点: Map集合是一个双列集合,一个元素包含两个值(一个key,一个value) Map集合中的元素,key和value的数据类型可以相同,也可以不同 Map集合中的元素,key是不允许重复的,value是可以重复的 Map集合中的元素,key和value是一一对应 java.util.HashMap<k,v&gt... 阅读详情

前言

项目上遇到过这样一个问题,需要对map集合中的对象按顺序进行存储。当时也没想那么多,直接一顿乱造,手写了个MyGoodMap,此类在继承了TreeMap的基础上,改写了比较器计算方式,使其变成始终一边倒的tree,很是高兴

最近偶然的机会,碰到了LinkedHashMap这个名词。看到这个词的第一眼,有种莫名的嘲讽在心中回荡,莫非…,我赶忙查阅了一下,没错,正是我要的类:可以按照顺序存储并查询的Map。二话不说,直接把MyGoodMap这个类替了下来(太丢我的脸)

今天决定研究一下,LinkedHashMap底层如何保证有序


HashMap的无序(微讲解便于后面理解,可选择跳过)

在研究之前,有必要了解一下HashMap底层是如何进行存储,如何查询遍历的

HashMap顾名思义按照hash算法进行散列存储对象的

HashMap散列方式:
散列值 = 要添加的键的hashCode % map大小

HashMap中的对象,底层都存储在一个Node<K,V>[]数组中(Node<K,V>封装了要存储的键值对,其实底层继承了Map.Entry,明白了吧),而上面得到的散列值,就对应了数组的下标,而要添加的键值呢,就存储在对应当前数组下标对应的Node单项链表中(下面解释为什么成了链表)。比如散列值=5,那么当前键值则存储在node[5]中

public class HashMap{
	...
    static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next; //指向下一个node
        ...
    }
}

数组中不是存的Node吗,怎么成了链表?原来,每个node对象都存在一个next属性(上图),不断指向下一个散列值相同的node,数组中实际存储的是链表中第一项,这样保证了所有相同散列值的node都能对应数组下标

我们来看put操作:

public V put(K key, V value) {
	//调用下方代码
    return putVal(hash(key), key, value, false, true);
}

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    //存储所有对象的底层数组
    Node<K,V>[] tab; 
    //当前要添加的对象(封装了键和值)
    Node<K,V> p; 
    //1.判断当前 散列值 对应数组下标的内容是否存在
    if ((p = tab[i = (n - 1) & hash]) == null)
    
    	//若不存在,则创建新node,作为当前下标的第一项(此处是接下来的重点!!!)
        tab[i] = newNode(hash, key, value, null);
        
    else {
        ...
            for (int binCount = 0; ; ++binCount) {
            	//2.如果数组中当前下标位置存在对象,
            	//  则将当前对象置于当前下标位置对应链表的尾部
            	//  此处,不断遍历node下一个指向对象,直到获取到最后一个
                if ((e = p.next) == null) {
                	//添加至尾部(下面的重点!!!)
                    p.next = newNode(hash, key, value, null);
                    ...
                }
                ...
                p = e;
            }
        ...
    }
    ...
}

根据上面代码,每次添加新的键值时,都会创建一个新的node,添加至node[]数组中(无论是作为真正存储的对象,也就是链表头部head,或是head.next关联的node)

看到这你可能有些乱(没关系),这乱七八糟的存储方式,是如何进行查询的呢?我们看一下底层迭代器如何遍历:

public void forEach(BiConsumer<? super K, ? super V> action) {
    ...
    	//1.遍历数组中的每一项(注意每一项对应了一个链表)
        for (int i = 0; i < tab.length; ++i) {
            for (Node<K,V> e = tab[i]; e != null; e = e.next)
                action.accept(e.key, e.value);
        }
    ...
}

还是很乱?就是很乱,以上遍历毫无顺序,完全根据散列位置进行遍历(看不懂没关系,知道乱就行),接下来咱们进入正题

LinkedHashMap的有序

LinkedHashMap继承了HashMap,它对HashMap进行了不大的改动

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

那么LinkedHashMap到底如何实现有序的呢?
首先,对HashMap中的Node进行了拓展,定义了新的内部类Entry<K,V>

public class 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);
        }
    }

加入了before,after两个属性,看样子LinkedHashMap打算自身维护一个双向链表
接下来让我们看看Entry对象的创建

//这是HashMap中的newNode方法
Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
    return new Node<>(hash, key, value, next);
}
//LinkedHashMap中覆盖了原有的newNode方法
@Override
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);
    //将当前node(entry)添加至链表尾部
    //注意:此处的链表是LinkedHashMap自身维护的双向链表
    linkNodeLast(p);
    return p;
}

private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
    LinkedHashMap.Entry<K,V> last = tail;
    tail = p;
    if (last == null)
        head = p;
    else {
        p.before = last;
        last.after = p;
    }
}

由此可以看出,LinkedHashMap每每在创建Entry时(也就是HashMap中的node),都会通过linkNodeLast方法偷偷维护一下自己的双向链表,毫无疑问,它一定也覆盖了HashMap中的forEach遍历方法:

@Override
public void forEach(BiConsumer<? super K, ? super V> action) {
	...
	//仅遍历自己的双向链表,完全摒弃数组
    for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
        action.accept(e.key, e.value);
    ...
}

再看HashMap原有的forEach:

public void forEach(BiConsumer<? super K, ? super V> action) {
    ...
    //通过数组下标+单向链表遍历
    for (int i = 0; i < tab.length; ++i) {
        for (Node<K,V> e = tab[i]; e != null; e = e.next)
            action.accept(e.key, e.value);
    }
    ...
}

破案,原来LinkedHashMap完全摒弃了原有底层数组维护的遍历方式,仅仅通过自己维护的双向链表进行不断向下遍历


最后

本人第一篇博客,一定存在很多问题,还请指正

AUTOSAR 导入DBC ISOLAR工程配置 ISOLAR工程配置-dbc导入 阅读详情

相关推荐

一文吃透 SAP HANA JSON Document Store:从架构到实战的完整指南

本文详细介绍了SAP HANA的JSON Document Store功能,包括其定位、架构、典型场景以及具体使用方法。文章首先分析了半结构化数据场景下传统关系模型的局限性,说明Document Store如何通过集合(Collection)存储JSON文档来提供灵活性。随后详细阐述了启用方式、SQL语法、与Column Store的联动机制,并提供了大量可直接复用的实战案例。特别强调了Document Store作为HANA内建服务的优势,包括企业级一致性、备份恢复等能力。最后给出了数据类型处理和跨引擎协

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 173

LinkedHashMap

1、LinkedHashMap有序的,是HashMap的子类,保留了插入的顺序,帮助我们实现了有序的HashMap。 2、其维护一个双向的链表,重写了HashMap的实体类Entry,实现能够将HashMap的数据组成一个双向列表,其存储的结构还是数组+链表的形式。 3、LinkedHashMap能够做到按照插入顺序或者访问顺序进行迭代顺序。 4、修改Entry对象,Entry新增了其上一...

zyzn1425077119的博客 2514

基于MATLAB的汽油发动机空燃比控制方法研究_毕业论文.pdf

基于MATLAB的汽油发动机空燃比控制方法研究_毕业论文.pdf

集合框架(六) -- 基于HashMap+LinkedList的有序Map:LinkedHashMap详解

前言: LinkedHashMap是使用HashMap+LinkedList来实现Map接口。和HashMap的区别在于它维持一个双端链表保存所插入访问的节点。链表定义了迭代顺序,默认情况下顺序就是键值插入到Map的顺序(插入顺序)。 构造方法为LinkedHashMap(int,float,boolean)的可用来创建一个按照访问顺序迭代的LinkedHashMap,按照最少访问到最多访问的顺序链接结点,这种LinkedHashMap可用来实现LRU缓存。 另外由于jdk8的HashMap的源码发生了变化

weixin_39724194的博客 1637

HashMap的无序性与LinkedHashMap有序

概述 HashMap HashMap存储元素是无序的,在通过Iterator遍历元素时也是无序的。 LinkedHashMap LinkedHashMap存储元素是无序的,在通过Iterator遍历元素时是有序的;put数据的顺序和输出顺序是一致的。 LinkedHashMap遍历元素的有序性,是采用了双向链表来保存节点。 HashMap源码分析 HashMap数据存储结...

https://github.com/xiangqians 3464

LinkHashMap集合能够使元素是有序

我们知道HashMap保证成对元素唯一,并且查询速度很快,可是成对元素存放进去是没有顺序的,那么我们要保证有序,还要速度快怎么办呢? 在HashMap下面有一个子类LinkedHashMap,它是链表和哈希表组合的一个数据存储结构。 java.util.LinkedHashMap extends HashMap 底层:哈希表+链表(记录元素的顺序) 由上面的代码示例可以看出,map只能做到key不重复,但是不能做到有序,而LinkedHashMap可以做到key不重复,而且元素有序

wang_qiu_hao的博客 1963

LinkedHashMap是如何实现有序的?

LinkedHashMap是怎么实现有序的? LinkedHashMap内部持有两个成员变量,head和tail,是双链表结点。  在插入数据时,仍然调用HashMap的put方法,将数据保存在原先的数组+链表/红黑树的数据结构中,又重写了newNode()方法,在newNode方法中,把新的结点插入双向链表。采用尾插法,保证了后插入的数据,位于双向链表的尾部。遍历时,从head结点开始遍历输出,输出顺序自然就和插入顺序一样。 LinkedHashMap迭代遍历的源码分析 常见的两种用iterator遍

知道这个有什么用? 3245

java 集合源码探究之 linkedhashmap有序的map,如何保证有序

类信息 属于hashmap的子类 内部的存储单元多了 before 、after 带有指向头部和尾部的指针 public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V> { static class Entry<K,V> extends HashMap.Node<K,V> { Entry<K,V> befor.

cyt 517

Java集合有序的Map实现——LinkedHashMap

引言 Map集合的特点是无序的,可重复的。而要想保证Map的有序,如何实现?Map结构下有多个实现类,HashMap是无序的,而LinkedHashMap继承了HashMap。下面就看一下LinkedHashMap是如何保证顺序的。 使用 下面是简单的使用,输出结果是按插入顺序排序的: Map<String,String> map = new LinkedHashMap<>...

Dream it Possible 2991

Java-概念】Map、Set集合存放是无序的,然而LinkedHashSet和LinkedHashMap集合有序,请解释是什么原因。

问题: Map、Set集合存放是无序的,然而LinkedHashSet和LinkedHashMap集合有序,请解释是什么原因。 相关概念: Set集合: 元素无序 (元素存取顺序不一致),元素不重复.Set集合保证元素的唯一性是靠重写HashCode()方法和equals()方法. HashSet,LinkedHashSet,TreesetHashSet: 此类实现Set接口,由哈希...

JockLiu的博客 711

有序的Map集合

我们通常使用的Map集合是HashMap,在大多数情况下HashMap可以满足我们的要求,但是HashMap有一个缺点:HashMap****是无序的,即其迭代顺序与其key或value的大小无关。而在某些情况下,如果我们需要Map集合里的元素有序,那么HashMap是不能满足我们的要求的。 那么有没有有序的Map集合呢?有,Java提供了两种有序的Map集合LinkedHashMap和TreeMap; **(一)**LinkedHashMap LinkedHashMap继承了HashMap,是HashM

m0_67400973的博客 3万+

Java 之 LinkedMap 集合

 LinkedHashMap 概述   java.util.LinkedHashMap<k,v>集合 extends HashMap<k,v>集合   LinkedHashMap的特点:     1. LinkedHashMap集合底层是哈希表+链表(保证迭代的顺序):查询速度快     2. LinkedHashMap集合是一个有序集合,存储元素和...

a804847944的博客 7922

集合框架(二)Map的三种体系、集合嵌套

1.特点(1)HashMap(由键决定):无序、不重复、无索引,(常用)(2)LinkedHashMap(由键决定):有序、不重复、无索引。 (3)TreeMap(由键决定):按大小默认升序、不重复、无索引。2.HashMap基于哈希表,HashMap跟HashSet的底层原理是一样的,实际上,Set集合就是基于Map实现...

weixin_41204131的博客 129

java map是有序的吗_java中map、set、list是否有序

首先我们应该清楚这个概念:这里的有序和无序不是指集合中的排序,而是是否按照元素添加的顺序来存储对象。list是按照元素的添加顺序来存储对象,因此是有序的。它的实现类ArrayList、LinkedList、Vector都是有序的。Map是无序的,它的存储结构是哈希表键值对,map中插入元素是根据key计算出的哈希值来存储元素的,因此他不是按照元素的添加顺序来存储对象的,所以Map是无序的。它的实现...

weixin_35940071的博客 8720

java linkhashmap_Java java.util.LinkedHashMap的用法以及案例

LinkedHashMap 在 HashMap 的基础上单独维护了一个具有所有数据的双向链表,该链表保证了元素迭代的顺序。代码实例:packagecom.what21;importjava.util.LinkedHashMap;importjava.util.Set;/**LinkedHashMap:是Map接口的哈希表和链接列表实现,具有可预知的迭代顺序。*由哈希表保证键的唯一性*由...

weixin_39787057的博客 1256

java中的有序和无序的含义

java知识

weixin_43930851的博客 957

实现Map接口的三类-HashMap-TreeMap-LinkedHashMap

LinkedHashMap的常用方法与HashMap和TreeMap类似,包括put、get、remove、containsKey、containsValue、size、isEmpty、keySet、values和entrySet等方法。TreeMap的特点是它能够保持键值对的有序性,这种有序性是基于键的自然顺序或者通过构造时提供的Comparator来实现的。LinkedHashMapJava集合框架中的一个类,它继承自HashMap,并在HashMap的基础上维护了元素的插入顺序或访问顺序。

全局可见 543

java linkedhashmap_java学习-hashMap和linkedHashMap

1、hashMap和linkedHashMap和treeMap1 * LinkedHashMap是继承于HashMap,是基于HashMap和双向链表来实现的。2 * HashMap无序;LinkedHashMap有序,可分为插入顺序和访问顺序两种。3 * 如果是访问顺序,那put和get操作已存在的Entry时,都会把Entry移动到双向链表的表尾(其实是先删除再插入)。4 * LinkedHa...

weixin_31908803的博客 312

Map、HashMap、LinkedHashMap各自特点

1). Map<k,v>集合 Map集合的特点: 1.Map集合是一个双列集合,一个元素包含两个值(一个key,一个value) 2.Map集合中的元素,key和value的数据类型可以相同,也可以不同 3.Map集合中的元素,key不允许重复,value允许重复 4.Map集合中的元素,key和value是一一对应 2). HashMap<k,v>集合 implement Map<k,v>接口 HashMap集合的特点: 1.HashMap集合底层是哈希表:

qq_44694100的博客 455

Map集合,HashMap和HashTable的区别,集合嵌套

Map集合,HashMap和HashTable的区别,集合嵌套 Map集合 (1)概述: Map称为双列集合,由键和值组成 假如我有这种数据 键 值 000---------------------张三 001----------------------李四 002----------------------王五 ​ Java针对这种键值映射...

weixin_45137957的博客 325

java:Map系列集合 - HashMap、LinkedHashMap、TreeMap的底层原理

java:Map系列集合 - HashMap、LinkedHashMap、TreeMap的底层原理

听海边涛声 597
下一篇: 【已解决】阿里云盘压缩包无法分享压缩文件分享限制
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值