说说 List,Set,Map 三者的区别?三者底层的数据结构?
- List
-
- 存储元素是有序的可重复的
- ArrayList:Object数组
- LinkedList:双向链表
- Vector:Object数组(线程安全)
- Set
-
- 存储的元素不可重复,顺序不一定
- HashSet(无序):基于HashMap实现
- TreeSet(有序):基于红黑树实现
- LinkedHashSet:(有序)HashSet子类,其内部是通过 LinkedHashMap 来实现的,双向链表记录了元素插入顺序。
- Map
-
- 存储键值对映射,键是无序的不重复的。
- HashMap:数组+链表/红黑树
- TreeMap:红黑树
- LinkedHashMap:在HashMap的基础上增加了节点之间的双向链表,实现顺序访问。
- HashTable:数组+链表。(线程安全)
有哪些集合是线程安全的?
Map: ConcurrentHashMap, HashTable(粗锁),synchronizedMap(粗锁)
List: CopyOnWriteArrayList, Vector(粗锁), synchronizedList
ConcurrentLinkedQueue
集合顶层是什么 以及各个接口的实现类
顶层Collection接口
Set接口:Hashset、Treeset、Linkedhashset
List接口:Arraylist、Linkedlist、vector(线程安全)、stack(继承vector)
Queue接口:priorityQueue(堆实现的优先队列)、ArrayDeque(双端队列数组实现)、Linkedlist(链表实现)
Map接口:Hashmap、Treemap(对键进行排序,基于红黑树)、LinkedHashMap、Hashtable(线程安全)
多看看这张图
List
ArrayList 与 LinkedList 区别?
三个方面
数据结构: ArrayList 底层使用的是 Object 数组;LinkedList 底层使用的是 双向链表 数据结构
插入/删除元素:LinkedList在首尾插入元素的时间复杂度为O(1) (在指定位置 i 插入和删除元素的话时间复杂度就为 O(n)) ,而ArrayList因为要移动前后元素所以平均修改复杂度为O(n)
查找元素:ArrayList可以以O(1) 的复杂度随机访问元素,LinkedList查找为O(n)
List集合的遍历相关问题?
- 使用foreach遍历时,list.remove方法会报错。因为看字节码 foreach编译后其实是使用iterator进行遍历,iterator遍历时要使用it.remove()方法进行移除。
- 或者使用list.removeIf( item->item.xxx )方法进行集合中元素过滤移除。
List可以遍历删元素吗?(云智)
可以,但是
- 增强for循环遍历时删会报错(增强for循环字节码层还是迭代器遍历)
-
- 要使用迭代器遍历,通过iterator.remove删除
- 或者removeIf 删除
ArrayList 可以添加 null 值吗?
ArrayList 中可以存储任何类型的对象,包括 null 值。不过,不建议向ArrayList 中添加 null 值, null 值无意义,会让代码难以维护比如忘记做判空处理就会导致空指针异常。
ArrayList如何删除某个元素?(云智)
- list.remove根据索引删
- list.remove根据对象删
- 迭代器遍历的时候通过it.remove删
- 用removeIf()删。
list.removeIf(element -> element.equals("Banana"));
ArrayList的扩容
四个点,初始容量0第一次add元素时分配为10,扩容因子1.5,扩容创建新数组并复制,每次添加前检查
- 初始容量:只有在添加第一个元素的时候才会分配,默认为10。
- 增长因子:1.5.
- 扩容触发条件:当ArrayList的size超过当前容量时,就会触发扩容操作。
- 扩容策略:ArrayList在扩容时,会创建一个新的更大容量的数组,并将原有元素复制到新数组中。
具体的扩容流程如下:
-
当向ArrayList添加add元素时,每次add会先检查当前容量是否足够。如果不足,则进行扩容操作。
-
扩容时,根据增长因子计算新的容量,并创建一个新的数组。
-
将原有数组中的元素复制到新数组中。
-
更新ArrayList内部的引用,指向新数组。
-
添加新元素到新数组中。
如何实现线程安全的List?
Vector(粗锁)Collections.synchronizedList()CopyOnWriteArrayList
CopyOnWriteArrayList介绍
- 采用写时复制的思想,达到读写的分离。读的是旧数据,写的是数据的副本。
- 只有写写的时候才加锁, 读写不互斥。
- 缺点:不能满足强一致性。因为读取的时候读的是旧数据。(类似于MVCC也不是强一致性的)(性能和强一致通常是一种权衡)