Java知识点学习(第6天)
说一下HashMap的Put方法
1.7和1.8版本的具体实现方式是不同的,但它们的大体流程是相似的:
- 根据key通过哈希算法与运算得出数组下标。
- 如果数组下标的位置元素为空,则将key和value封装成为Entry对象(1.7为Entry对象,1.8为Node对象)并放入该位置。
- 如果数组下标元素不为空,则要分情况进行讨论:
如果是1.7版本,则先判断是否需要扩容,如果需要扩容就进行扩容,如果不需要扩容就直接生成Entry对象,并使用头插法添加到当前位置的列表中。
如果是1.8版本,则先判断当前位置上的Node类型,看是红黑树Node,还是链表Node
- 如果是红黑树Node,则将key和value封装成为一个红黑树节点并添加到红黑树中,这个过程会判断红黑树中是否存在当前的key,如果存在则更新value。
- 如果此位置上的Node节点为链表节点,则将key和value封装为一个链表Node并通过尾插法插入链表的最后位置,因为是尾插法,需要遍历整个链表,在遍历的过程中会判断是否存在当前的key,如果存在则更新value值,在遍历完链表后,将新的链表Node插入链表中,插入链表后,会看当前链表的节点个数,如果大于等于8,那么会将链表变为红黑树。
- 将key和value封装为Node插入链表和红黑树后,再判断是否需要进行扩容,如果需要扩容就扩容,不需要就直接结束PUT方法。
深拷贝和浅拷贝
浅拷贝是指,只拷贝基本的数据类型的值,以及实例对象的引用地址,内部属性类指向的是同一个对象。简单来说,就是原数据发生变更,新数据也会发生变更。
深拷贝是值,既会拷贝基本数据类型的值,也会针对实例对象的引用地址所指向的对象进行复制,内部的属性指向的不是同一个对象。简单说,就是原数据发生变更,数据仍保持不变。
HashMap的扩容机制原理
1.7版本:
- 生成新的数组
- 遍历老数组中的每个位置上的链表上的每个元素
- 取每个元素的key,并基于新的数组长度计算出在新数组中的下标
- 将元素添加到新数组中
- 所有元素转移完后,将新数组赋值给HashMap对象的table属性中
1.8版本:
- 生成新的数组
- 遍历老数组中的每个位置上的列表或红黑树
- 如果是链表,则直接将链表中的每个元素重新计算下标,并添加到新数组中
- 如果是红黑树,则先遍历红黑树,先计算出红黑树每个元素对应在新数组的下标位置,统计每个下标的元素个数,如果元素个数超过了8个,则生成一个新的红黑树,并将根节点添加到新数组的对应位置;如果元素的个数没有超过8,则生成一个链表,并将链表的头节点添加到新数组的对应位置
- 所有元素转移完后,将新数组赋值给HashMap对象的table属性中
CopyOnWriteArrayList的底层原理是怎样的
因为ArrayList是线程不安全的,所以引入线程安全的CopyOnWriteArrayList。
- 首先CopyOnWriteArrayList内部是通过数组进行实现的,在添加元素时,会复制一个新的数组,写操作在新数组中,读操作在原数组中。
- 写操作会加锁,防止出现并发写入丢失数据,写操作结束后,会将原数组指向新数组。
- CopyOnWriteArrayList允许在写操作时读取数据,大大提高读的性能,因此适合读多写少的应用场景,但是CopyOnWriteArrayList会比较占内存,同时可能读到的数据不是最新的数据,所以不适合实时性要求很高的场景。