精华内容
下载资源
问答
  • HashMap源码分析 —— HashMap为什么是无序的?

    千次阅读 多人点赞 2019-06-13 23:49:50
    HashMap源码解析HashMap为什么是无序的?问题:为什么说HashMap无序的?但是某种情况下看起来却好像是有序的?原因(源码分析):数据结构:功能实现最后,我们应该知道为什么HashMap无序的了吗?最后再次感谢...

    HashMapd的put方法为什么是无序的?

    参考:https://blog.csdn.net/login_sonata/article/details/76598675 (有删改和补充)

    题外话:最近有朋友问我一个问题,也是我曾经初学HashMap时问过老师的问题。所以我打算写一篇博客来回答,以前看过不少博客,但是没想过写。初次尝试,请多指教。

    问题:为什么说HashMap 是无序的?但是某种情况下看起来却好像是有序的?

    某种情况指的是下面这种情况:

    HashMap<Integer,Integer> hashMap = new HashMap<>();
         hashMap.put(1,1);
         hashMap.put(2,2);
         hashMap.put(3,3);
         hashMap.put(4,4);
         hashMap.put(5,5);
         hashMap.put(6,6);
    
     for (Integer s: hashMap.keySet()) {
        System.out.print(hashMap.get(s) + " ");
     }
    

    如果按照上面的方式向HashMap中添加数据,那输出结果肯定是:

    1 2 3 4 5 6
    

    看到这个输出结果, 有人就会对HashMap的无序产生怀疑了,这里为什么是有序的呢?
    (你也可以改变添加数据的顺序,比如先添加 “2” 再添加 “1”,最后结果还是这样)

    带着这个疑问我们再来看一个例子:

    HashMap<Integer,Integer> hashMap = new HashMap<>();
        hashMap.put(1,1);
        hashMap.put(2,2);
        hashMap.put(3,3);
        hashMap.put(4,4);
        hashMap.put(5,5);
        hashMap.put(6,6);
        hashMap.put(65536,65536);
        
    for (Integer s: hashMap.keySet()) { 
       System.out.print(hashMap.get(s) + " ");
    }
    

    我们思考一下,你觉得会输出什么? 还会是1 2 3 4 5 6 65536这样的顺序吗?

    运行结果是:1 65536 2 3 4 5 6

    看完这个例子之后,很显然HashMap是无序的,因为它有自己的一套算法。

    那我们回过来想,为什么在第一个例子中会出现HashMap有序的情况呢?

    想要知道答案就来分析HashMap源码吧。

    原因(源码分析):

    数据结构源码实现 两个方面来讲解(以JDK 1.8为例)

    数据结构:

    HashMap的数据结构由 数组 + 链表 + 红黑树(JDK 1.8版本才加入的红黑树)
    在这里插入图片描述
    我们看这幅图应该需要明白两个问题:

    1. 数据底层具体存储的元素是什么(就是上图中的小黑点是什么)?
      从源码中(HashMap类第395行)可以看到一个字段 Node[] table(即Hash桶数组),
      这个字段就是上图中的数组, Node 类型就是小黑点, Node之间通过 next字段链接成链
      表。下面是Node类型的具体代码:
     static class Node<K,V> implements Map.Entry<K,V> {
        final int hash; // 用来定义数组索引位置
        final K key;
        V value;
        Node<K,V> next; // 链表中下一个Node
    
        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }
     }
    
    1. 这样存储的方式有什么优点?
      HashMap是使用哈希表来存储的,为了解决Hash冲突使用的是链地址法(相关文档: Hash讲解)。链地址法,简单来说,就是数组加链表的结合。在每个数组元素上都一个链表结构,当数据被Hash(就是调用源码中的hash方法,在 源码的第337行)后,得到被扰乱的hash值,然后通过位与运算获取数组下标(调用 源码的第630行进行位与运算操作得到数组下标),最后把数据放在该数组下标链表上。

    例如程序执行下面代码:

         hashMap.put(65536,65536);
    

    系统将调用 “65536” 这个 keyhashCode() 方法得到其 hash值 (该方法适用于每个Java对象),然后再通过Hash算法的后两步运算(高位运算和取模运算,下文有介绍)来定位该键值对的存储在数组中的位置,有时两个key会定位到数组中相同的位置,表示发生了Hash碰撞(碰撞之后就会生成链表)。当然Hash算法计算结果在越分散均匀,Hash碰撞的概率就越小,map的存取效率就会越高。

    如果哈希桶数组很大,即使较差的Hash算法也会比较分散,如果哈希桶数组数组很小,即使好的Hash算法也会出现较多碰撞,所以就需要在空间成本和时间成本之间权衡,其实就是在根据实际情况确定哈希桶数组(Node[] table)的大小,并在此基础上设计好的hash算法减少Hash碰撞。所以好的Hash算法和扩容机制至关重要。

    在理解Hash和扩容流程之前,我们得先了解下HashMap的几个字段
    从HashMap的默认构造函数源码可知,构造函数就是对下面几个字段进行初始化:

    	int threshold;             // 扩容阈值 
    	final float loadFactor;    // 负载因子
    	transient int modCount;  // 出现线程问题时,负责及时抛异常
    	transient int size;     // HashMap中实际存在的Node数量
    

    首先,Node[] table的初始化长度length(默认值是16),Load factor为负载因子(默认值是0.75),threshold是HashMap所能容纳的最大数据量的Node(键值对)个数threshold = length * Load factor
    也就是说,在数组定义好长度之后,负载因子越大,所能容纳的键值对个数越多。

    结合负载因子的定义公式可知,threshold就是在此Load factorlength(数组长度)对应下允许的最大元素数目,超过这个数目就重新resize(扩容),扩容后的HashMap容量是之前容量的两倍。默认的负载因子0.75是对空间和时间效率的一个平衡选择,建议大家不要修改,除非在时间和空间比较特殊的情况下,比如内存空间很多而又对时间效率要求很高,可以降低负载因子Load factor的值;相反,如果内存空间紧张而对时间效率要求不高,可以增加负载因子loadFactor的值,这个值可以大于1。

    size这个字段其实很好理解,就是HashMap中实际存在的键值对数量。注意和table的长度length容纳最大键值对数量threshold的区别。而modCount字段主要用来记录HashMap内部结构发生变化的次数。强调一点,内部结构发生变化指的是结构发生变化,例如put新键值对,但是某个key对应的value值被覆盖不属于结构变化。

    在HashMap中,哈希桶数组table的长度length大小必须为2的n次方(一定是合数),这是一种非常规的设计,常规的设计是把桶的大小设计为素数。相对来说素数导致冲突的概率要小于合数,具体证明可以参考 为什么一般hashtable的桶数会取一个素数

    这里存在一个问题,即使负载因子和Hash算法设计的再合理,也免不了会出现拉链过长的情况,一旦出现拉链过长,则会严重影响HashMap的性能。于是,在JDK1.8版本中,对数据结构做了进一步的优化,引入了红黑树。而当链表长度太长(默认超过8)时,链表就转换为红黑树,利用红黑树快速增删改查的特点提高HashMap的性能。

    功能实现

    HashMap的内部功能实现很多,本文主要从根据key获取哈希桶数组索引位置、put方法的详细执行、扩容过程三个具有代表性的点深入讲解。

    1. 确定哈希桶数组索引位置
      不管增加、删除、查找键值对,定位到哈希桶数组的位置都是很关键的第一步。前面说过HashMap的数据结构是数组和链表的结合,所以我们当然希望这个HashMap里面的元素位置尽量分布均匀些,当我们用hash算法求得这个位置的时候,马上就可以知道对应位置的元素就是我们要的,不用遍历链表,大大优化了查询的效率。HashMap定位数组索引位置,直接决定了hash方法的离散性能。先看看源码的实现(方法一 + 方法二):
    	// 方法一,jdk1.8 & jdk1.7都有:
    	static final int hash(Object key) { 
    		int h; 
    		return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); 
    	} 
    
    
    	// 方法二,jdk1.7有,jdk1.8没有这个方法,把这个取消了,置换成了一行代码。
    	static int indexFor(int h, int length) {
     		return h & (length-1); 
    	}
    
    这里的Hash算法本质上就是三步:
    (1)取key的hashCode值,h = key.hashCode();
    (2) 高位参与运算,h ^ (h >>> 16);
    (3) 取模运算,h & (length-1)。

    对于任意给定的对象,只要它的hashCode()返回值相同,那么程序调用方法一所计算得到的Hash码值总是相同的。我们首先想到的就是把hash值对数组长度取模运算,这样一来,元素的分布相对来说是比较均匀的。但是,模运算的消耗还是比较大的,在HashMap中是这样做的:调用方法二来计算该对象应该保存在table数组的哪个索引处

    这个方法非常巧妙,它通过h & (table.length -1)来得到该对象的保存位,而HashMap底层数组的长度总是2的n次方,这是HashMap在速度上的优化。当length总是2的n次方时,h& (length-1)运算等价于对length取模,也就是h%length,但是&比%具有更高的效率。

    在JDK1.8的实现中,优化了高位运算的算法,通过hashCode()的高16位异或低16位实现的:(h = k.hashCode()) ^ (h >>> 16),主要是从速度、功效、质量来考虑的,这么做可以在数组table的length比较小的时候,也能保证考虑到高低Bit都参与到Hash的计算中,同时不会有太大的开销。

    下面举例说明下,ntable[]的长度:
    在这里插入图片描述

    1. 分析HashMap的put方法
      HashMap的put方法执行过程可以通过下图来理解,自己有兴趣可以去对比源码更清楚地研究学习,源码在本文最后可以找到。
      在这里插入图片描述

    2. 扩容机制

    HashMap对象内部的数组无法装载更多的元素时,对象就需要扩大数组的长度,以便能装入更多的元素。当然Java里的数组是无法自动扩容的,方法是使用一个新的数组代替已有的容量小的数组

    首先举个例子直观感受下扩容过程。假设了我们的hash算法就是简单的用key mod 一下表的大小(也就是数组的长度)。其中的哈希桶数组table的size=2, 所以key = 3、7、5,put顺序依次为 5、7、3。在mod 2以后都冲突在table[1]这里了。这里假设负载因子 loadFactor=1,即当键值对的实际大小size 大于 table的实际大小时进行扩容。接下来的三个步骤是哈希桶数组 resize成4,然后所有的Node重新rehash的过程。
    在这里插入图片描述

    简单说就是换一个更大的数组重新映射。下面我们讲解下JDK1.8做了哪些优化。经过观测可以发现,我们使用的是2次幂的扩展(指长度扩为原来2倍),所以,元素的位置要么是在原位置,要么是在原位置再移动2次幂的位置。看下图可以明白这句话的意思,n为table的长度,图(a)表示扩容前的key1和key2两种key确定索引位置的示例,图(b)表示扩容后key1和key2两种key确定索引位置的示例,其中hash1是key1对应的哈希与高位运算结果
    在这里插入图片描述

    元素在重新计算hash之后,因为n变为2倍,那么n-1的mask范围在高位多1bit(红色),因此新的index就会发生这样的变化:
    在这里插入图片描述

    因此,我们在扩充HashMap的时候,只需要看原来的hash值新增的那个bit是1还是0就好了,是0的话索引没变,是1的话索引变成“原索引+oldCap”,可以看看下图为16扩充为32的resize示意图:
    在这里插入图片描述
    这个设计省去了重新计算hash值的时间,而且同时,由于新增的1bit是0还是1可以认为是随机的,因此resize的过程,均匀的把之前的冲突的节点分散到新的bucket了

    最后,我们应该知道为什么HashMap是无序的了吗?

    总而言之,就是出现了哈希冲突所以才导致无序。

    感谢我所参考文档的提供者们。

    展开全文
  • Hash算法 在说HashMap之前先来了解一下Hash算法。在数据结构中学习过线性表,我们知道在线性表中查询一个值最坏的情况可能是从头遍历到尾,其平均时间复杂度O(n),并不理想,而hash表能将查询的时间复杂度降O(1),...

    Hash算法

    在说HashMap之前先来了解一下Hash算法。在数据结构中学习过线性表,我们知道在线性表中查询一个值最坏的情况可能是从头遍历到尾,其平均时间复杂度为O(n),并不理想,而hash表能将查询的时间复杂度降为O(1),因为Hash算法会通过hash函数计算出地址直接取值,其查询次数只有一次。

    通过下面例子简单了解一下hash表的查询方式,下面是一个hash表,首先假设hash函数为n%10,hash函数计算出来的结果就是其hash表中该元素的地址,所以10、13和26的存储结果如下图。

    b22e10f53669803adc708e6ce4c83efc.png

    可能实际的hash函数能很好地避免地址的冲突,但是还是有地址冲突的可能性,比如10和20。java中hashMap解决方式是"链地址法",如下图,将hash值冲突的值使用链表连接起来,这样查询到地址0的时候就会依次比较10和20,看到底哪个才是要找的。

    9e4ca63c015ee3b0fb32831fd7ff6e44.png

    HashMap

    下面来了解什么是HashMap,"**Map集合即Key-Value的集合,前面加个Hash,即散列,无序的。所以HashMap即散着的,无序的Key-Value集合**",这是最初我看到的一个对我个人而言比较好理解的解释。当我们使用的hashMap的键值为对象的时候可能就要重写hashCode和eqals。

    先看下面一段代码

    public class User {private String name;private String password;public User() {super();}public User(String name, String passed) {super();this.name = name;this.passed = passed;}public class Demo {public static void main(String[] args) {User user1=new User("name", "passed");User user2=new User("name", "passed");//定义hashMapHashMap hashmap=new HashMap();//添加hashmap.put(user1, "value1");//通过user2获取添加的valueString str=hashmap.get(user2);System.out.println("输出结果:"+str);}}

    通过代码可知,实体类User中只有两个属性name和password,Main函数中声明了两个User的实例,他们的两个属性都是相同的,那我们现在希望使用user2取出user1对应的value值,看下是否能成功。

    运行结果:

    输出结果:null

    为什么不是想象的结果呢。因为当我们向hashmap添加user1时,hashmap首先会调用User的hashCode来计算hash值作为地址,因为本例中没有重写hashCode方法,所以hashmap是调用的Object的hashCode方法来计算hash值,Object中hashCode计算出来的hash值其实就是对象的地址,所以user1与user2存储的的地址肯定不同,下面就重写User的hashCode

    @Overridepublic int hashCode() {final int prime = 31;int result = 1;result = prime * result + ((name == null) ? 0 : name.hashCode());result = prime * result + ((passed == null) ? 0 : passed.hashCode());return result;}

    运行结果:

    输出结果:null

    运行结果还是null,既然重写了hashCode方法,寻找user2时候理论上是能够正确寻找到user1存储地址的,为什么结果还是null?这里就要了解一下HashMap找到地址后动作。前面已经说过,java中HashMap解决hash值冲突,使用了链地址法,也就是在通过user2获取user1的value的时候并不是通过User重写的hashCode计算出user2的地址后就直接从该地址中取相应的值,而是还要调用equals方法来进行比较,这就和没有重写hashCode造成错误的原因类似了,没有重写equals方法,就要被迫调用Object类的equals方法,而Object类的equals方法是直接比较两个对象的内存地址,所以输出结果是null。

    现在重写equals方法

    @Overridepublic boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;if (getClass() != obj.getClass())return false;User other = (User) obj;if (name == null) {if (other.name != null)return false;} else if (!name.equals(other.name))return false;if (passed == null) {if (other.passed != null)return false;} else if (!passed.equals(other.passed))return false;return true;}

    运行结果:

    输出结果:value1

    成功。

    总结

    由上面的分析可知,当键值为对象类型的时候就需要重写hashCode和equals方法。

    展开全文
  • 最近遇到的一个坑-HashMap(为什么HashMap无序的) 1. 缘起-简单的demo 最近使用HashMap时候遇到一个问题 ,就是HashMap本身是无序的,怎么理解呢,可以拿如下代码来进行测试 Map<Integer, String> map = new...

    1. 缘起-简单的demo

    最近使用HashMap时候遇到一个问题 ,就是HashMap本身是无序的,怎么理解呢,可以拿如下代码来进行测试

      		Map<Integer, String> map = new HashMap<>();
            for (int i = 0; i < 10; i++) {
                map.put(i, "value");
            }
            map.forEach(((integer, value) -> {
                System.out.println(integer);
            }));
    

    看起来没问题吧,输出结果

    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    

    你可能会说 ,这不正常么

    2. 缘生-意外的结果

    我们把代码稍作修改

      			Map<Integer, String> map = new HashMap<>();
            for (int i = 0; i < 10; i++) {
                map.put(10-i, "value");
            }
            map.forEach(((integer, value) -> {
                System.out.println(integer);
            }));
    

    这下在看下结果

    在我意料中应改是10 9 8 …1 ,没错吧 ,我想的是这样,但是实际上呢

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    

    很奇怪吧,HashMap是无序的, 为什么会有这种情况呢

    3. 缘解-探索为什么HashMap无序

    (debug过程,

    1. 搜索HashMap.的put方法,
    2. 点进putVal方法,
    3. 可以顺带看下put中的对key的处理过程hash()
    4. 然后就可以看到下面罗列的代码了)

    盲猜一下,HashMap在增加数据的时候,自己给数据增加了排序,所以才不是我们想要的结果了

    /**
         * Associates the specified value with the specified key in this map.
         * If the map previously contained a mapping for the key, the old
         * value is replaced.
         *
         * @param key key with which the specified value is to be associated
         * @param value value to be associated with the specified key
         * @return the previous value associated with <tt>key</tt>, or
         *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
         *         (A <tt>null</tt> return can also indicate that the map
         *         previously associated <tt>null</tt> with <tt>key</tt>.)
         */
        public V put(K key, V value) {
            return putVal(hash(key), key, value, false, true);
        }
    
    

    看下HashMap的put方法 ,可以看出来,存储的时候会对key进行hash

    稍等片刻…等我debug找下原因

    进入putVal()方法

     /**
         * Implements Map.put and related methods.
         *
         * @param hash hash for key
         * @param key the key
         * @param value the value to put
         * @param onlyIfAbsent if true, don't change existing value
         * @param evict if false, the table is in creation mode.
         * @return previous value, or null if none
         */
        final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                       boolean evict) {
            Node<K,V>[] tab; Node<K,V> p; int n, i;
            if ((tab = table) == null || (n = tab.length) == 0)
                n = (tab = resize()).length;
            if ((p = tab[i = (n - 1) & hash]) == null)
                tab[i] = newNode(hash, key, value, null);
            else {
                Node<K,V> e; K k;
                if (p.hash == hash &&
                    ((k = p.key) == key || (key != null && key.equals(k))))
                    e = p;
                else if (p instanceof TreeNode)
                    e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
                else {
                    for (int binCount = 0; ; ++binCount) {
                        if ((e = p.next) == null) {
                            p.next = newNode(hash, key, value, null);
                            if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                                treeifyBin(tab, hash);
                            break;
                        }
                        if (e.hash == hash &&
                            ((k = e.key) == key || (key != null && key.equals(k))))
                            break;
                        p = e;
                    }
                }
                if (e != null) { // existing mapping for key
                    V oldValue = e.value;
                    if (!onlyIfAbsent || oldValue == null)
                        e.value = value;
                    afterNodeAccess(e);
                    return oldValue;
                }
            }
            ++modCount;
            if (++size > threshold)
                resize();
            afterNodeInsertion(evict);
            return null;
        }
    

    可以看出map其实是一个 Node<K,V>[] tab 的数组对象

    而这边数组对象肯定是有顺序的,

    在上面代码的第而得else中可以看出

    tab[i = (n - 1) & hash])

    数组的索引是 根据 (n - 1) & hash进行计算的

    所以这个时候,其实数据在存储到HashMap的时候由于对key的hash值不同,在存储的时候时候顺序就不是固定的了

    而对key的hash算法如下(尽管我看不懂,但是我还是要贴一下代码)

    static final int hash(Object key) {
            int h;
            return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
        }
    

    额代码就是上面个代码

    所以说, 这样hash值不同,在存储到tab的时候顺序就不是固定的,所以综上所述,HashMap不是有序的

    4. 怎么避免想要一个有序的map

    这个时候可以使用LinkedHashMap

    LinkedHashMap在我使用的时候是有序的,毕竟其包含链表么, 链表就是,数据之间会有顺序,无论是单向链表还是双向链表,都包含,所指向的下一个节点,这次踩坑就到此为止了,下次在用Map在出现这种顺序问题,我就自己起锅炖了自己

    展开全文
  • HashMap无序

    千次阅读 2013-12-02 18:15:53
    设计初衷主要是为了解决键值(key-value)对应关联的,HashMap的优势是可以很快的根据键(key)找到该键对应的值(value),但是我们在使用的过程中需要注意一下,HashMap是一种无序的存储结构。HashMap的实现是假定...

    一、 说明

    HashMap是基于哈希表Map的实现。设计初衷主要是为了解决键值(key-value)对应关联的,HashMap的优势是可以很快的根据键(key)找到该键对应的值(value),但是我们在使用的过程中需要注意一下,HashMap是一种无序的存储结构。HashMap的实现是假定元素是放在一个圆形的环上,每次put进来的元素根据其hashCode计算该元素在圆环上索引,把该元素放到合适的位置。

    二、 Put和get关键方法

    public V put(K key, V value) {

    if (key == null)

    return putForNullKey(value);

    当传入的key为null时,则直接调用putForNullKey方法,在索引为0的位置压入该value值,这也就是HashMap为什么支持压入null值。

    int hash = hash(key.hashCode());

    int i = indexFor(hash, table.length);

    这两个方法是很牛X的地方,第一个方法是根据key的hashCode利用hash方法生成一个hash值,然后通过indexFor函数获取table的索引值。

    是的,没错,HashMap就是通过table的结构来存储数据的。HashMap最牛X的地方是能根据hash值计算出正确的索引,比较深奥。下面是put的整个方法:

    public V put(K key, V value) {

    if (key == null)

    return putForNullKey(value);

    int hash = hash(key.hashCode());

    int i = indexFor(hash, table.length);

    for (Entry<K,V> e = table[i]; e != null; e = e.next) {

    Object k;

    if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {

    V oldValue = e.value;

    e.value = value;

    e.recordAccess(this);

    return oldValue;

    }

    }

    modCount++;

    addEntry(hash, key, value, i);

    return null;

    }

    实际上,如果插入的数据在HashMap中已经存在,则调用put函数的时候会返回oldValue。当我们在使用get函数的时候,同样是根据key的hashCode,从table中的对应索引位置提取该值并返回。

    到这里,我们可以看到HashMap是通过key的hashCode来计算索引的,与元素放入的先后顺序没有什么关系,所以我们在使用HashMap的时候,千万不要寄希望于HashMap中的数据与我们压入的数据的先后顺序一致。如果要保证压入的顺序一致,可以使用LinkedHashMap对象。

    三、 HashMap与HashTable的区别

    1. HashMap允许put进来一个null元素,HashTable则不允许,put进来null值后抛异常

    2. HashMap的一些操作(如put)没有同步,而HashTable的很多操作都是加上同步操作(增加了synchronized关键字)

    展开全文
  • HashMap源码分析,为什么是无序的?

    千次阅读 2017-01-17 22:51:54
    疑问:HashMap无序的,怎么做到的?先看一个现象Map, Integer> m = new HashMap(); for(int i=0; i; i++) { m.put("key"+i, i); } System.out.println(m);结果{key1=1, key2=2, key0=0, key5=5, key6=6, key3=3,...
  • HashMap的put方法为什么无序

    千次阅读 2018-05-04 11:04:01
    HashMap的put方法为什么无序 参考文章 [1]https://blog.csdn.net/qq_34908167/article/details/79269488 [2]https://www.cnblogs.com/liujinhong/p/6576543.html [3]...
  • HashMap无序序列

    2014-10-10 20:11:40
    HashMap的设计初衷主要是为了解决键值(key-value)对应的关联的,HashMap的优势是可以很快的根据键(key)找到该键对应的值(value),但是我们在使用的过程中需要注意一下,HashMap是一种无序的存储结构。HashMap...
  • 在日常中,hashMap的put方法存储是无序的,不能根据自定义存放顺序来,如果需要有序则使用LinkedHashMap,如下面代码, put时的顺序: 12 -> 3 ->5 -> 16 -> 20 -> 21 循环遍历后的结果: 16 -...
  • HashMap无序的测试

    2021-03-20 17:19:21
    HashMap无序的,无序的,无序的 我们在做测试的时候有时候会出现, 我创建一个HashMap, 但打印的时候,好像它是按照key来排序了一样,如下例 HashMap<Integer, String> ihm = new HashMap<>(); ihm.put...
  • HashMap的到底是有序还是无序

    万次阅读 2020-06-27 11:18:54
    HashMap的到底是有序还是无序前提问题背景HashMap的一些特性问题分析结论再结论 前提 首先说明:HashMap不保证插入顺序,但是循环遍历时,输出顺序是不会改变的。 代码说明: public class HashMapTest { public ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 49,857
精华内容 19,942
关键字:

hashmap为什么是无序的