Collection
Map
List和Set集合的区别
List集合:有序,可重复,允许存储多个null值,可以通过迭代器和get元素下标的方式取得指定元素,底层实现有数组和链表两种形式。
Set集合:无序,元素唯一,最多一个null值,只能用迭代器取得元素,底层基于Map实现,实际上set集合的元素就是map的键。
ArrayList和LinkedList的区别
ArrayList:
基于动态数组,连续内存存储,可以通过下标访问,查询数据快。
扩容机制:默认容量10,超过10进行扩容,扩容后容量是之前的1.5倍。
适用于查询场景,增删数据需要复制元素移动,效率低下。
LinkedList:
基于双向链表,分散的内存中存储,增删数据快。
LinkedList存储元素会携带当前元素和下一个元素的指针,增删数据时只需将两个数据的指针断开并重新建立连接即可。
适用于增删场景,查询需要逐一遍历,性能消耗极大。
Vector集合
底层是数组,相对于ArrayList它是线程安全的,扩容量是之前的两倍
知道哪些线程安全的List吗?
List list = new Vector<>();
List strings = new CopyOnWriteArrayList<>();
List strings = Collections.synchronizedList(new ArrayList<>());
CopyOnWriteArrayList原理
首先它是一个线程安全的List,底层是复制数组来实现的写入时复制,当我们往一个容器中添加元素的时候,会先将容器复制出一个新的,往新的容器中加元素,再将旧数组的引用指向新的。
优点:
可以对CopyOnWrite容器进行并发的读而不用加锁,因为当前容器不会改。
缺点:
内存占用,由于CopyOnWrite的写时复制机制,在进行写操作时,内存里会同时驻扎两个对象内存。
容器不能保证数据的实时一致性,可能读到旧数据。
怎么给List排序呢?
可以使用List自带的sort方法,也可以使用Collections.sort(list)方法。
怎么在遍历ArrayList时移除一个元素?
如果使用foreach删除元素,会触发一个快速失败问题,可以使用迭代器的remove方法避免快速失败问题。
hashmap和hashtable的区别;底层实现是什么样的?
1、安全方面:hashmap的方法没有加synchronized修饰,线程不安全,hashtable是安全的。
2、null值:hashmap允许存储null键null值,hashtable不支持。
hashmap底层结构(jdk1.8):数组+链表(解决hash冲突)+红黑树
转换红黑树的条件:链表高度达到8数组长度大于64,此时链表就会转换为红黑树,当高度为6时又会转换成链表,中间有个差值7防止链表与树之间的频繁转换。
存值的过程:
1、计算哈希值,确定元素要存储的数组下标位置
2、是否有哈希冲突(计算出来的下标位置是否有值)
3、有冲突的情况,先进行equals比较,是同一个覆盖该元素,没有冲突直接创建node存入数据。
hashmap底层结构(jdk1.7):数组+单向链表
put元素时判断长度是否达到阈值,如果达到先扩容再把元素保存。
concurrentHashMap原理
数据结构:synchronized+CAS+数组+链表(Node)+红黑树
查找、替换、赋值操作都使用CAS(compare and swap)
锁:锁链表的head节点不影响其他元素的读写,锁的粒度更细、效率更高。
扩容时,阻塞所有的读写操作并发扩容。
读操作没有锁
Node的val和next使用volatile修饰,读写线程对该变量互相可见。
数组用volatile修饰,保证扩容时被读线程感知。
快速失败(fail-fast)和安全失败(fail-safe)的区别
快速失败:
发生场景:在对容器进行遍历的过程中,对数据进行修改。会抛出ConcurrentModificationException异常。
安全失败:安全失败的这种遍历是基于容器的一个克隆,对容器修改不会影响遍历。java.util.concurrent包下的容器都是安全失败,多线程下并发使用,并发修改。
常见的使用安全失败机制的容器有ConcerrentHashMap和CopyOnWriteArrayList等。