关于Java HashMap自动排序的简单剖析
1.HashMap概述
HashMap是无序的,这里无序的意思是你取出数据的顺序与你存入数据的顺序不同
2.发现问题
当尝试向HashMap中存入int类型的key,可以看到在输出的时候会自动排序
HashMapmap=newHashMap<>(); map.put(3,"asdf"); map.put(2,"asdf"); map.put(1,"asdf"); map.put(6,"asdf"); map.put(5,"asdf"); map.put(4,"asdf"); map.put(8,"asdf"); map.put(9,"asdf"); map.put(7,"asdf"); map.put(0,"asdf");
输出
3.实现原理
我们都知道,HashMap是数组加链表实现的,在链表长度大于8的时候将链表转化为红黑树
数组加链表画一下模型图是这样的,黑色的是数组,橙色的是链表,遍历HashMap的key的时候,先遍历第一列,然后第二列。。。
4.翻看源码
HashMap的默认数组长度为16,默认负载因子是0.75,意思就是当数组内不为null的元素大于(数组长度*负载因子)的时候就会拓容数组
如果数组长度和负载因子都是默认值,那当在数组中存入第13个元素后就会拓容16*0.75=12
/** *Thedefaultinitialcapacity-MUSTbeapoweroftwo. */ staticfinalintDEFAULT_INITIAL_CAPACITY=1<<4;//aka16 /** *Theloadfactorusedwhennonespecifiedinconstructor. */ staticfinalfloatDEFAULT_LOAD_FACTOR=0.75f;
再读一下put方法和hash方法
publicVput(Kkey,Vvalue){ returnputVal(hash(key),key,value,false,true); } //获取key的hash,当key为int类型的时候hash=key的值 staticfinalinthash(Objectkey){ inth; return(key==null)?0:(h=key.hashCode())^(h>>>16); } finalVputVal(inthash,Kkey,Vvalue,booleanonlyIfAbsent, booleanevict){ Node[]tab;Node p;intn,i; //tab就是HashMap的数组,这句话就是初始化数组 if((tab=table)==null||(n=tab.length)==0) n=(tab=resize()).length; //如果根据hash值来判断将此元素放在什么位置,如果数组当前位置 //为空直接存放,成为一个长度为一的链表 if((p=tab[i=(n-1)&hash])==null)//================== tab[i]=newNode(hash,key,value,null); //如果不是,则将当前元素放在当前位置下元素的后边形成链表 else{ Node e;Kk; if(p.hash==hash&& ((k=p.key)==key||(key!=null&&key.equals(k)))) e=p; elseif(pinstanceofTreeNode) e=((TreeNode )p).putTreeVal(this,tab,hash,key,value); else{ for(intbinCount=0;;++binCount){ if((e=p.next)==null){ p.next=newNode(hash,key,value,null); if(binCount>=TREEIFY_THRESHOLD-1)//-1for1st treeifyBin(tab,hash); break; } if(e.hash==hash&& ((k=e.key)==key||(key!=null&&key.equals(k)))) break; p=e; } } if(e!=null){//existingmappingforkey VoldValue=e.value; if(!onlyIfAbsent||oldValue==null) e.value=value; afterNodeAccess(e); returnoldValue; } } ++modCount; if(++size>threshold) //拓容数组 resize(); afterNodeInsertion(evict); returnnull; }
5.分析
17行:if((p=tab[i=(n-1)&hash])==null)
(n-1)&hash
n-1就是数组的length-1,现在数组长度是16,
15&hash,
例如,现在存入key为1,
15转成二进制为1111
1转成二进制为0001,
所以i=15&1=1;
现在1就存放在数组下标为1的位置
如果放2,那就放在数组下标为2的位置,
如果再存放17的话,
1501111
1710001
15&17=1;因为数组下标1的位置有上一次存放的key为1的元素,所以就将key=17的元素挂在key=1的下边,
这是遍历HashMap的key就会变成1,17,2
顺序就会乱掉,现在数组的长度是16,已使用的是2,还没有达到拓容那一步,
6.验证
下边的代码是存放11个数据,拓容要存入第13个数据时进行拓容
HashMapmap=newHashMap<>(); map.put(3,"asdf"); map.put(2,"asdf"); map.put(1,"asdf"); map.put(6,"asdf"); map.put(5,"asdf"); map.put(4,"asdf"); map.put(8,"asdf"); map.put(9,"asdf"); map.put(7,"asdf"); map.put(0,"asdf"); map.put(17,"saf"); //map.put(10,"saf"); //map.put(11,"saf"); for(inti:map.keySet()){ System.out.println("key="+i); } System.out.println("map.size()==============="+map.size());
下边这段代码的输出结果就是
和分析的一样,
如果再添加两个数据,使其拓容
HashMapmap=newHashMap<>(); map.put(3,"asdf"); map.put(2,"asdf"); map.put(1,"asdf"); map.put(6,"asdf"); map.put(5,"asdf"); map.put(4,"asdf"); map.put(8,"asdf"); map.put(9,"asdf"); map.put(7,"asdf"); map.put(0,"asdf"); map.put(17,"saf"); //map.put(10,"saf"); //map.put(11,"saf"); for(inti:map.keySet()){ System.out.println("key="+i); } System.out.println("map.size()==============="+map.size());
输出是
又排好了顺序
7.结论
当所有key的hash的最大值<数组的长度-1时HashMap可以将存入的元素按照key的hash从小到大排序
不过这个发现没有什么用就是了,不过看了一天源码收获不少,还看到好几种没见过的写法
到此这篇关于关于JavaHashMap自动排序简单剖析的文章就介绍到这了,更多相关JavaHashMap自动排序内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。