1.String、StringBuffer、StringBuilder的区别
1.String底层是用final修饰的字符数组,不能够更改,如果尝试更改,会新生成一个字符串对象,StringBuffer和
StringBuilder是可变的;
2.StringBuffer是线程安全的,StringBuilder是线程不安全的,所以在单线程环境下StringBuffer的效率低于StringBuilder。
2. ArrayList和LinkedList有哪些区别
1.首先,他们的数据结构不同,ArrayList底层是基于数组实现的,LinkedList底层是基于链表实现的;
2.基于数据结构的不同,ArrayList更适合随机查找,LinkedList更适合删除和添加;
3.另外ArrayList和LinkedList都实现了List接⼝,但是LinkedList还额外实现了Deque接⼝,所以LinkedList还可以当做队列来使⽤。
3. CopyOnWriteArrayList的底层原理是怎样的
- CopyOnWriteArrayList底层是基于数组来实现的,在向CopyOnWriteArrayList添加元素时,会复制一个新的数组,写操作在新数组上进行,读操作在原数组上进行;
- 写操作会加锁,防止出现并发写入丢失数据的问题
- 写操作结束之后,会把原数组指向新数组
- CopyOnWriteArrayList允许在写操作的时候进行读操作,提高了读的性能,适合读多写少的场景,但是CopyOnWriteArrayList比较占用内存,可能读到的数据不是实时最新的数据,所有不适合实时性要求高的场景
4. HashMap的扩容原理
1.7 版本
- 先生成新数组
- 遍历老数组中的每个位置上的链表上的每个元素
- 取每个元素的key,并基于新数组长度,计算出每个元素在新数组中的下标
- 将元素添加到新的数组中
- 所有元素转移完之后,将新数组赋值给hashmap对象的table属性
1.8 版本
- 先生成新的数组
- 遍历老数组中每个位置上的链表或红黑树
- 如果是链表,则直接将链表中的每一个元素重新计算下标,并添加到新数组中
- 如果是红黑树,则先遍历红黑树,计算出红黑树中每个元素对应新数组中的下表位置
a. 统计每个下标位置的元素个数
b. 如果该位置下的元素个数超过了8,则生成一个新的红黑树,并将根节点添加到新的数组对应的位置
c. 如果该位置下的元素个数没有超过8,则生成一个链表,并将链表的头节点添加到新数组对应的位置 - 所有元素转移完之后,将新数组赋值给hashmap对象的table属性
5.ConcurrentHashMap的扩容机制
1.7版本
- 1.7版本的ConcurrentHashMap是基于Segment分段实现的
- 每个Segment相当于一个小型的HashMap
- 每个Segment内部进行扩容,和HashMap的扩容机制相似
- 先生成新的数组,然后转移元素到新数组中
- 扩容判断也是每个segment内部单独判断,判断是否超过阈值
1.8版本
- 1.8版本的ConcurrentHashMap不在基于Segment实现
- 当某个线程进行put时,如果发现ConcurrentHashMap正在进行扩容,那么该线程一起进行扩容
- 如果某个线程进行put时,发现没有正在扩容,则将k-v添加到ConcurrentHashMap中,然后判断是否超过阈值,超过了则进行扩容
- ConcurrentHashMap是支持多个线程同时扩容的
- 扩容之前也是先生成一个新的数组
- 在转移元素时,先将原数组分组,将每组分给不同的线程进行元素的转移,每个线程负责一组或多组的元素转移工作