精华内容
下载资源
问答
  • HashMap的到底是有序还是无序

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

    前提

    首先说明:HashMap不保证插入顺序,但是循环遍历时,输出顺序是不会改变的。
    代码说明:

    public class HashMapTest {
    
        public static void main(String[] args) {
            HashMap<String, String> map = new HashMap<>();
            map.put("aaa", "aaa");
            map.put("bbb", "bbb");
            map.put("ccc", "ccc");
    
            System.out.println("第一次输出:");
            for (Map.Entry<String, String> entry : map.entrySet()) {
                System.out.println(entry.getKey());
            }
            System.out.println("\n第二次输出:");
            for (Map.Entry<String, String> entry : map.entrySet()) {
                System.out.println(entry.getKey());
            }
        }
    }
    

    输出:

    第一次输出:
    aaa
    ccc
    bbb
    
    第二次输出:
    aaa
    ccc
    bbb
    

    问题背景

    项目开发过程中,遇到类似的如下代码,即,两个Map,put的key值相同,但是两个Map最后循环遍历输出的key顺序却不一样

    public class HashMapTest {
    
        public static void main(String[] args) {
            HashMap<String, String> map1 = new HashMap<>();
            map1.put("123", "aaa");
            map1.put("23456", "bbb");
    
            System.out.println("map1的循环遍历");
            for (Map.Entry<String, String> entry : map1.entrySet()) {
                System.out.println(entry.getKey());
            }
    
            HashMap<String, String> map2 = new HashMap<>(map1.size());
            map2.put("123", "aaa");
            map2.put("23456", "bbb");
            System.out.println("map2的循环遍历");
            for (Map.Entry<String, String> entry : map2.entrySet()) {
                System.out.println(entry.getKey());
            }
        }
    }
    

    代码输出为:

    map1的循环遍历
    123
    23456
    map2的循环遍历
    23456
    123
    

    HashMap的一些特性

    • HashMap的数据结构:数组+单链表,当存在hashCode相同的不同对象时,会将value以单链表的形式,往后追加。数组加快访问速度,单链表解决hash值冲突
    • 调用put方法时,发生了什么:根据key的hashCode,计算出将key放入数组的index下标,index= (数组长度 - 1) & hashCode
    • HashMap循环遍历的顺序:根据数组顺序+单链表顺序进行输出。虽然遍历时,用的EntrySet,但是可以简单理解为,两层循环输出数据,外层循环为遍历数组,内层循环为遍历单链表
    • HashMap在初始化时,默认初始容量为16,以及默认的扩容因子0.75(在此处就不详细介绍了)

    问题分析

    基于如上HashMap的特性,仔细看map2的初始化,会发现

            HashMap<String, String> map2 = new HashMap<>(map1.size());
    

    即:map2的初始化容量=map1.size()。
    这个设置,导致相同的key,在put到map2时,计算出的index值,与map1中的不一样
    因为计算index时,依赖于数组的长度(参考上面:put方法调用说明)

    结论

    若两个Map的初始化容量不一致,就算同时插入相同的key,最后输出的顺序,不一定一直

    再结论

    代码规范性挺重要的,想要依赖Map保持有序性,请使用LinkedHashMap

    展开全文
  • 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<String, String> map2 = new HashMap<>();
  • hashmap无序有序

    万次阅读 多人点赞 2018-12-02 17:55:41
    上代码 public static void main(String[] args) { ... map = new HashMap&lt;&gt;(); map.put("3","1"); map.put("4","1"); map.put("1","1&

    上代码

     public static void main(String[] args) {
            Map<String,String> map = new HashMap<>();
            map.put("3","1");
            map.put("4","1");
            map.put("1","1");
            for (int i = 0; i < 100; i++) {
                System.out.println("++++++++++++++++++");
                map.forEach((k, v) -> {
                    System.out.println(k);
                });
                System.out.println("++++++++++++++++++");
            }
        }

    看结果:

    ++++++++++++++++++
    1
    3
    4
    ++++++++++++++++++
    ++++++++++++++++++
    1
    3
    4
    ++++++++++++++++++
    ++++++++++++++++++
    1
    3
    4
    ++++++++++++++++++
    ++++++++++++++++++
    1
    3
    4
    ++++++++++++++++++
    ++++++++++++++++++
    1
    3
    4
    ++++++++++++++++++

    无序:存进去的顺序是:3,4,1.取出来是:1,3,4

    有序:循环一百遍输出都是:1,3,4

    总结:map内保存内容的顺序不一定与放进去顺序一致,这叫无序。

               内容不变,取出来顺序一定不变,这叫有序。

    展开全文
  • 今天学习Map集合时书上说HashMap是无序的,TreeMap是有序的(有序无序是针对key的),但是实际去敲的时候发现不是这样,有时HashMap是有序的,有时TreeMap是无序的。于是就做了以下测试来探究: 测试代码: //第一...

    今天学习Map集合时书上说HashMap是无序的,TreeMap是有序的(有序无序是针对key的),但是实际去敲的时候发现不是这样,有时HashMap是有序的,有时TreeMap是无序的。于是就做了以下测试来探究:

    测试代码:

      		//第一组测试:HashMap和TreeMap的key都是String类型的
            Map<String, Integer> hashMap = new HashMap<String, Integer>();
            hashMap.put("8", 3);
            hashMap.put("9", 2);
            hashMap.put("10", 6);
            hashMap.put("11", 2);
            hashMap.put("12", 6);
            System.out.println("第一组测试:HashMap和TreeMap的key都是String类型的");
            System.out.println("HashMap:");
            System.out.println(hashMap);
    
            Map<String, Integer> treeMap = new TreeMap<String, Integer>();
            treeMap.put("8", 399);
            treeMap.put("9", 67);
            treeMap.put("10", 2);
            treeMap.put("11", 67);
            treeMap.put("12", 2);
            System.out.println("TreeMap:");
            System.out.println(treeMap);
            System.out.println();
    
            //第二组测试:HashMap和TreeMap的key都是Integer类型的
            Map<Integer, Integer> hashMap2 = new HashMap<Integer, Integer>();
            hashMap2.put(9, 67);
            hashMap2.put(10, 2);
            hashMap2.put(8, 399);
            hashMap2.put(11, 67);
            hashMap2.put(12, 2);
            System.out.println("第二组测试:HashMap和TreeMap的key都是Integer类型的");
            System.out.println("HashMap:");
            System.out.println(hashMap2);
    
            Map<Integer, Integer> treeMap2 = new TreeMap<Integer, Integer>();
            treeMap2.put(9, 67);
            treeMap2.put(10, 2);
            treeMap2.put(8, 399);
            treeMap2.put(11, 67);
            treeMap2.put(12, 2);
            System.out.println("TreeMap:");
            System.out.println(treeMap2);
            System.out.println();
    
            //第三组测试:HashMap和TreeMap的key都是String类型的,但是都是英文字母
            Map<String, Integer> hashMap3 = new HashMap<String, Integer>();
            hashMap3.put("apple", 1);
            hashMap3.put("orange", 2);
            hashMap3.put("pear", 3);
            System.out.println("第三组测试:HashMap和TreeMap的key都是String类型的,但是都是英文字母");
            System.out.println("HashMap:");
            System.out.println(hashMap3);
    
            Map<String, Integer> treeMap3 = new TreeMap<String, Integer>();
            treeMap3.put("apple", 1);
            treeMap3.put("orange", 2);
            treeMap3.put("pear", 3);
            System.out.println("TreeMap:");
            System.out.println(treeMap3);
    

    运行结果:

    第一组测试:HashMap和TreeMap的key都是String类型的
    HashMap:
    {11=2, 12=6, 8=3, 9=2, 10=6}
    TreeMap:
    {10=2, 11=67, 12=2, 8=399, 9=67}
    
    第二组测试:HashMap和TreeMap的key都是Integer类型的
    HashMap:
    {8=399, 9=67, 10=2, 11=67, 12=2}
    TreeMap:
    {8=399, 9=67, 10=2, 11=67, 12=2}
    
    第三组测试:HashMap和TreeMap的key都是String类型的,但是都是英文字母
    HashMap:
    {orange=2, apple=1, pear=3}
    TreeMap:
    {apple=1, orange=2, pear=3}
    

    通过测试可以总结出:

    当key值是Integer类型时,HashMap和TreeMap都是“有序”的;
    当key值是String类型的数字时,HashMap和TreeMap都是“无序”的;
    当key值是String类型的字母时,HashMap是“无序”的,TreeMap是有序的。

    后来又去查了一下TreeMap是按照什么来排序的,结果:TreeMap按照key的字典顺序来排序(升序)
    这样就对上了,之前感觉当key值是String类型的数字时,HashMap和TreeMap都是“无序”的,是因为如果按照数字排序的话确实是无序的,但是TreeMap所谓的有序并不是按照数字顺序,而是字典顺序。

    展开全文
  • HashMap无序 LinkedHashMap有序

    千次阅读 2015-08-04 17:12:28
    HashMap为什么无序?  HashMap数据结构是table[entry], entry是一个链表结构,数据的每个元素是一个链表。不同key,但具有相同hashcode便会落在table[hashcode]的链表上。 即,hashMap顺序是由key的hash值决定...
  • 文章目录HashSet有序还是无序,如何改为顺序输出一、HashSet对于Integer包装类型有序二、对于引用类型,如何进行顺序输出 HashSet有序还是无序,如何改为顺序输出 众所周知,HashSet本质就是HashMap的实现,只取用...
  • HashMap无序性与LinkedHashMap的有序

    千次阅读 2019-11-26 12:17:00
    HashMap存储元素是无序的,在通过Iterator遍历元素时也是无序的。 LinkedHashMap LinkedHashMap存储元素是无序的,在通过Iterator遍历元素时是有序的;put数据的顺序和输出顺序是一致的。 LinkedHashMap遍历...
  • hashmap无序的但是实际输出有序

    千次阅读 2020-01-14 21:16:34
    HashMap无序是指不会记录插入的顺序,也不会根据特定规则进行排序; 但是HashMap存值的时候会根据key的hashCode()来计算存储的位置(位置是散列的,所以说其无序); 你使用的key是String类型,String重写的...
  • HashMap为什么是无序?   HashMap的数据结构是table[entry],entry是一个链表结构,数据的每个元素是一个链表。不同key,但是具有相同hashcode会落在table[hashcode]的链表上 当使用iterator遍历时,使用如下code...
  • HashMap无序与LinkedHashMap有序

    千次阅读 2011-11-10 16:51:07
    HashMap为什么是无序?   HashMap的数据结构是table[entry],entry是一个链表结构,数据的每个元素是一个链表。不同key,但是具有相同hashcode会落在table[hashcode]的链表上 当使用iterator遍历时,使用如下code...
  • HashMap无序

    2020-04-12 12:23:24
    HashMap无序的 一、说明 HashMap是基于哈希表Map的实现。HashMap的设计初衷主要是为了解决键值(key-value)对应的关联的,HashMap的优势是可以很快的根据键(key)找到该键对应的值(value),但是我们在使用...
  • 由于map集合时无序的,我们接触到最多的集合中只有List集合时有序的.通过查了查,发现有一种map(LinkedHashMap)集合时有序的,可以做到按照用户放入集合的顺序取出集合中的元素. LinkedHashMap介绍: 简单的介绍...
  • HashMap无序,LinkedHashMap会多一个链表存储元素顺序,TreeMap实现SortedMap接口可以对元素进行排序。
  • 在有些特定的场景下,我们需要使用到有序的键值对——LinkedHashMap 比如我下面的场景,要求添加ip+端口号的键值对,同时接口要求端口从0开始。 我们比较一下haspMap和LinkedHashMap的区别,直接看代码结果: ...
  • 今天迭代hashmap时,hashmap并不能按照put的顺序,迭代输出值。用下述方法可以: HashMap<String,String> hashmap = new LinkedHashMap<String,String>(); HashSet的内容如何排序 方法一:...
  • HashMap无序的测试

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

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

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 38,320
精华内容 15,328
关键字:

hashmap有序还是无序