统一登录入口,国外网站搜索引擎优化方案,网站放到云服务器上怎么做,wordpress+机械模板下载首先我们运行一段代码#xff1a;此时运行#xff0c;程序正常#xff0c;接下来我们将注释放开#xff1a;此时运行发现#xff0c;OOM了#xff1a;为什么new出来HashMap的时候并没有报OOM#xff0c;而是在第一次进行put操作的时候才报的OOM#xff1f;我们来看下ma… 首先我们运行一段代码此时运行程序正常接下来我们将注释放开此时运行发现OOM了为什么new出来HashMap的时候并没有报OOM而是在第一次进行put操作的时候才报的OOM我们来看下map的源码。设置数组长度、扩容阈值创建Map时可以指定元素个数也可以不指定。先来看下不指定元素个数的构造方法public HashMap() { this.loadFactor DEFAULT_LOAD_FACTOR; // 设置加载因子为0.75}static final float DEFAULT_LOAD_FACTOR 0.75f;当进行put操作时会进行resize扩容操作final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node[] tab; Node p; int n, i; if ((tab table) null || (n tab.length) 0) //初始table为空 n (tab resize()).length; //触发resize操作 //省略... } //省略...}transient Node[] table;看下resize扩容的核心代码final Node[] resize() { Node[] oldTab table; //初始table为空 int oldCap (oldTab null) ? 0 : oldTab.length; //所以oldCap为0 int oldThr threshold; //初始threshold为0 所以oldThr为0 int newCap, newThr 0; if (oldCap 0) { //省略... } else if (oldThr 0) //省略... else { newCap DEFAULT_INITIAL_CAPACITY; //设置新数组长度为16 newThr (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); //设置新map可容纳元素个数为16*0.75 } if (newThr 0) { //省略... } threshold newThr; //为threshold赋值为newThr 代表扩容后的map的可容纳元素个数上限 当map中元素个数超过threshold会触发扩容操作 SuppressWarnings({rawtypes,unchecked}) Node[] newTab (Node[])new Node[newCap]; table newTab; //设置table为扩容后的新数组 //省略... return newTab;}static final int DEFAULT_INITIAL_CAPACITY 1 4;如果指定元素个数例如MapString,String map new HashMapString,String(17);public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); //加载因子0.75}public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity 0) throw new IllegalArgumentException(Illegal initial capacity: initialCapacity); if (initialCapacity MAXIMUM_CAPACITY) initialCapacity MAXIMUM_CAPACITY; if (loadFactor 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException(Illegal load factor: loadFactor); this.loadFactor loadFactor; //加载因子0.75 this.threshold tableSizeFor(initialCapacity); //设置threshold为最接近initialCapacity的2次幂的值}static final float DEFAULT_LOAD_FACTOR 0.75f;调用tableSizeFor方法为threshold赋值看下tableSizeFor的代码static final int tableSizeFor(int cap) { int n cap - 1; n | n 1; n | n 2; n | n 4; n | n 8; n | n 16; return (n 0) ? 1 : (n MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n 1;}这个函数是用来对你申请的容量进行处理让他变成最接近你申请的容量的2次幂的大小这里注意假如你申请的容量为0最后处理的结果会变成1代表着你最小的容量为1。我们自己测试一下tableSizeFor(17)32public static void main(String[] args) { int number 17; System.out.println(tableSizeFor(number)); //32}public static int tableSizeFor(int number) { int n number - 1; n | n 1; n | n 2; n | n 4; n | n 8; n | n 16; return (n 0) ? 1 : (n MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n 1;}补充算术右移()右移是按最左边(高位)来补的(即如果是1就补1,如果是0就补0,不改变该位的值)。另一种说法正数右移高位补0负数右移高位补1。逻辑右移()不管最左边一位是0还是1,都补0。另一种说法无论是正数还是负数高位通通补0。同样的当put元素时会触发map的扩容操作。接下来看下指定元素个数时Map扩容的核心源码。final Node[] resize() { Node[] oldTab table; int oldCap (oldTab null) ? 0 : oldTab.length; //初始table为空 oldCap0 int oldThr threshold; //此时threshold已经有值(2的n次幂) int newCap, newThr 0; if (oldCap 0) { //省略... } else if (oldThr 0) newCap oldThr; //扩容后map的数组长度 else { 省略... } if (newThr 0) { float ft (float)newCap * loadFactor; //设置扩容后map的threshold newThr (newCap MAXIMUM_CAPACITY ft (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE); } threshold newThr; SuppressWarnings({rawtypes,unchecked}) Node[] newTab (Node[])new Node[newCap]; table newTab; //省略... return newTab;}总结一下如果没有指定元素个数则扩容后的数组长度默认为16如果指定元素个数则扩容后的数组长度为与指定的元素个数最接近的2次幂的值比如指定元素个数为17,则数组长度为32。threshold的取值都是固定数组长度*加载因子(0.75,0.75并不是一个绝对的只是在时间和空间上可能map的效率最高)。再回到一开始的问题new的时候没有OOM但是在put的时候却OOM了因为在new的时候只是设置threshold值而且设置的值还是比最大整数大的2次幂,在扩容的时候需要分配数组内存所以OOM了。数据迁移上面其实只是分析了resize操作关于设置数组长度、扩容阈值的代码真正扩容后数据迁移都省略了接下来看下数据迁移部分final Node[] resize() { //省略... if (oldTab ! null) { for (int j 0; j oldCap; j) { //遍历老数组 Node e; if ((e oldTab[j]) ! null) { oldTab[j] null; if (e.next null) newTab[e.hash (newCap - 1)] e; //如果老数组的某一索引位置没有链表则将计算该索引位置的元素在新数组的索引位置 else if (e instanceof TreeNode) ((TreeNode)e).split(this, newTab, j, oldCap); else { //如果索引位置有链表 Node loHead null, loTail null; Node hiHead null, hiTail null; Node next; do { next e.next; if ((e.hash oldCap) 0) { //将索引位置的链表拆分成loHead-loTail和hiHead-hiTail两个链表顺序保持不变 if (loTail null) loHead e; else loTail.next e; //尾插法 loTail e; } else { if (hiTail null) hiHead e; else hiTail.next e; hiTail e; } } while ((e next) ! null); if (loTail ! null) { loTail.next null; newTab[j] loHead; //新数组索引位置放入loHead链表 } if (hiTail ! null) { hiTail.next null; newTab[j oldCap] hiHead; //新数组索引位置原始数组长度位置放入hiHead链表 } } } } } return newTab;}举例说明下e.hasholdCap0来区分该放到loHead还是hiHead链表下面是我的理解0 1 0 0 0 8 //假设原数组长度8 即oldCap80 0 0 1 1 3 j0 //假设原数组0索引位置存在3 6 12 三个数组成的链表0 0 1 1 0 6 j0 3-6 //经e.hasholdCap计算 3和6结果均为0 所以组成loHead3-60 1 1 0 0 12 j8 [08] //而12与8与操作结果不等于0 所以组成hiHead12在扩容时 将3-6放入新数组的0索引位置将12放入新数组的8索引位置这样的好处是不用计算链表的每一个元素在新数组对应的索引位置了同时也保持了元素在链表中的顺序。