1.1ArrayList
接口实现ArreyList集合实现了RandomAccess(随机访问)接口不适合使用迭代器来进行遍历,
重写了序列化方法,transient Object[] elementDate(存储数据的节点) 节省了50%的序列化空间
近似1.5倍扩容 时间复杂度为o1
初始长度为10会进行1次扩容
System.arreyCopy();进行浅拷贝扩容模式
System.arraycopy()方法详解 system.arraycopy(详解)
JAVA基础----java泛型中E,T,?的区别(泛型详解)
一、深度复制和浅度复制的区别
Java数组的复制操作可以分为深度复制和浅度复制,简单来说深度复制,可以将对象的值和对象的内容复制;浅复制是指对对象引用的复制。(浅拷贝扩容一个框用原来数据内存地址指向框,深拷贝重新创建一个数据并将数据拷贝到新数组当中)
1.1使用java为我们提供的工具类进行生产ArrayList
这样不行(否)
1.1第一种
使用Arrays.saList();进行创建list集合
自己创建了一个私有的ArrayList底层是继承了AbstractList,没有重写add方法,而父类AbstractList的add方法会直接抛出异常
抛出异常改为:UnsupportedOperationException
在什么情况下使用Arrays.saList();呢?
元素个数固定使用
在 不可做删除 不可做增加,可以做修改和查询的情况下使用
王校长总结:
1.2第二种
数组里面元素是对象所以需要赋值为null值,赋为null值之后对象为不可达对象会被GC回收掉,防止内存的泄露问题
1.2 Clear()
多实用clear方法进行清除操作完成空间的复用,每次clear()只会将数据内容清空掉,不会清空list原有的扩容长度
认为利用了addAll();里面的边缘性返回值,导致了非异常-异常
如果list为null那么‘’;
就会抛出异常(在生产环境不能抛出异常这样)
问题在于:返回值有歧义
1.3ArrayList序列化问题
集合当中元素存放在被transient修饰的Object[]当中,元素不会被序列化
HashMap
put方法的hash计算
hashCode右移16位异或自身
1.1 comparableClassFor()
ParameterizedType代表类似ArrayList<String> 包裹类型判断
奇妙算法2n次方的向上
1.2 get()方法
右移16位异或自身
getNode();方法 判断当中加上Hash计较,是为了加快运算效率,hash是int类型的
//Get方法 public V get(Object key) { HashMap.Node<K,V> e; return (e = getNode(hash(key), key)) == null ? null : e.value; } //get方法 final HashMap.Node<K,V> getNode(int hash, Object key) { HashMap.Node<K,V>[] tab; HashMap.Node<K,V> first, e; int n; K k; if ((tab = table) != null && (n = tab.length) > 0 &&//判断传入key是否在HashMap当中 (first = tab[(n - 1) & hash]) != null) { if (first.hash == hash && // always check first node ((k = first.key) == key || (key != null && key.equals(k))))//数组链表头节点是对应的key,则直接将Value返回 return first; if ((e = first.next) != null) {//如果链表头不为对应数据则进行非空判断 if (first instanceof HashMap.TreeNode)//判断类型是否为树 return ((HashMap.TreeNode<K,V>)first).getTreeNode(hash, key); do { //为链表,do while循环取出链表当中的数据 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } //如果没有返回null值 return null; } |
1.3 put();
//put元素核心方法putVal();详解 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)//计算出put元素的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 HashMap.TreeNode)//判断书否是树结构 e = ((HashMap.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);//长度大于8时进行树化处理 break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))//数据在链表当中存在直接返回null break; p = e; } } if (e != null) { // existing mapping for key//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; } |
1.4 resize();扩容方法
扩容为底层数组的长度
final HashMap.Node<K,V>[] resize() { HashMap.Node<K,V>[] oldTab = table;//old 16 长度 int oldCap = (oldTab == null) ? 0 : oldTab.length;//16 int oldThr = threshold;//12 int newCap, newThr = 0; if (oldCap > 0) { if (oldCap >= MAXIMUM_CAPACITY) {//超过2的30次方直接不管了 /** * threshold 这个值,只与扩容有关,他的大小不会限制其他操作 */ threshold = Integer.MAX_VALUE;//2147483647 return oldTab; } /** * 将数组扩容2倍 */ else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) newThr = oldThr << 1; // double threshold//thr也进行扩容二倍 } else if (oldThr > 0) // initial capacity was placed in threshold newCap = oldThr; else { // zero initial threshold signifies using defaults 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;//真正的开始给threhod进行了newThr的赋值 @SuppressWarnings({"rawtypes","unchecked"}) HashMap.Node<K,V>[] newTab = (HashMap.Node<K,V>[])new HashMap.Node[newCap]; table = newTab;//真正的开始给table 进行newTab的赋值
//=========================开始节点的迁移======================================== if (oldTab != null) { for (int j = 0; j < oldCap; ++j) { HashMap.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 HashMap.TreeNode) /** * 如果进行map扩容我们的树肯定会被拆掉 性能消耗很大 * 实际情况下,我们的HashMap很难达到 红黑树。 Hash 十分散列;我们业务,不可能 * 也不能够,不允许 在一个map放那么多元素。 * 如果我需要从db里取出2的30次方个元素,需要用hashMap进行处理那么怎么办? * 我们可以使用clear();方法进行清除数据而不会清除扩容空间,这样我们就减少了扩容对性能的损耗 * 我们只需要进行分批处理clear();在处理 */ ((HashMap.TreeNode<K,V>)e).split(this, newTab, j, oldCap); else { // preserve order HashMap.Node<K,V> loHead = null,//old 链表的 头节点 loTail = null; //old 链表的尾节点 HashMap.Node<K,V> hiHead = null,// new 链表的 投节点 hiTail = null; //new 链表的尾节点 HashMap.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 {//如果不等于0.变化位置,将该元素重新方到新的table当中 if (hiTail == null) hiHead = e; else hiTail.next = e; hiTail = e; } } while ((e = next) != null);
//真正添加 if (loTail != null) { loTail.next = null; newTab[j] = loHead; //这是真的将数据放入newTab里面了 } if (hiTail != null) { hiTail.next = null; newTab[j + oldCap] = hiHead; //这是真的将数据放入newTab里面了 } } } } } return newTab; }
//sparkStream大数据处理 |