
复杂度

List
为什么数组索引从0开始?从1开始不行吗?

ArrayList源码分析
源码基于jdk1.8
成员变量

构造函数


添加和扩容操作

第2次-10次

第十一次次添加数据-扩容

ArrayList底层的实现原理是什么

ArrayList list = new ArrayList(10)中的list扩容几次

该构造函数只是声明和实例了一个ArrayList,指定了容量为10,未扩容
如何实现数组和List之间的转换


用Arrays.asList转List后,如果修改了数组内容,list受影响吗?
受影响

只涉及了引用,没有创建数组
List用toArray转数组后,如果修改了List内容,数组受影响吗?
不受影响

复制了数组中的内容到新数组中


ArrayList和LinkedList的区别是什么?
HashMap

hashmap的实现原理
hashmap的数据结构:底层使用hash表数据结构,即数组和链表和红黑树

hashmap的jdk1.7和jdk1.8有什么区别?

hashmap的put方法的具体流程?
常见属性

当put元素时

- hashmap是懒加载,在创建对象时并没有初始化数组
- 在无参的构造函数中,设置了默认的加载因子是0.75

put方法具体流程

hashmap的扩容机制

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
| final Node<K,V>[] resize() { Node<K,V>[] oldTab = table; int oldCap = (oldTab == null) ? 0 : oldTab.length; int oldThr = threshold; int newCap, newThr = 0; if (oldCap > 0) { if (oldCap >= MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return oldTab; } else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) newThr = oldThr << 1; } else if (oldThr > 0) newCap = oldThr; else { newCap = DEFAULT_INITIAL_CAPACITY; newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } if (newThr == 0) { float ft = (float)newCap * loadFactor; newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE); } threshold = newThr; @SuppressWarnings({"rawtypes","unchecked"}) Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; table = newTab; if (oldTab != null) { for (int j = 0; j < oldCap; ++j) { Node<K,V> 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<K,V>)e).split(this, newTab, j, oldCap); else { Node<K,V> loHead = null, loTail = null; Node<K,V> hiHead = null, hiTail = null; Node<K,V> next; do { next = e.next; if ((e.hash & oldCap) == 0) { 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; } if (hiTail != null) { hiTail.next = null; newTab[j + oldCap] = hiHead; } } } } } return newTab; }
|
讲一讲hashmap的扩容机制

hashmap的寻址算法

计算对象的hashCode()
再调用hash()方法进行二次哈希,hashcode值右移16位再异或运算,让哈希分布更为均匀
最后(capacity-1)&hash得到索引
为什么hashmap的数组长度一定是2的次幂?

hashmap在1.7情况下的多线程死循环问题


举例:线程1和线程2的变量e和next都引用了这两个节点

线程2扩容后,由于头插法,链表顺序颠倒,但是线程1的临时变量e和next还引用了这个两个节点

第一次while循环,因为此时(上图)e指向A,所以A进行迁移,那么下一个e的位置指向此时的next位置,也就是B,而next下一个位置则指向现在next节点的next,也就是A

第二次循环,B进行迁移,e节点的下一个位置则是next节点,也就是A;next节点的下一个位置则是此时next节点的下一个next,null
第三次循环,此时e指向的还是A,进行迁移,头插法,e的下一个位置是null;next也是null,出现循环

参考问答
