Java 容器面试题
List Set Map 的区别
List和Set继承同一个接口Collection。
List中的元素是有序的可重复的,ArrayList默认初始化为容量为10的空列表,没有负载因子。
Set 中的元素是无序不可重复的,HashSet默认初始化容量为16,负载因子为0.75。
Map 中key是无序不可重复的,value是无序可重复的。
ArrayList和LinkedList的区别
ArrayList底层是数组结构,支持快速的随机访问,但是中间增删效率低,因为涉及到数组的拷贝。
LinkedList底层是链表,不支持随机访问,增删速度快,因为只需要链表的断开和链接(每个元素都有上一个和下一个元素的地址)。
ArrayList的扩容
空构造器初始化时ArrayList是一个空数组,长度为0,在第一次执行add方法时,创建长度为10的数组,每次扩容时扩容原来的1.5倍。
计算方式如下:
HashSet的扩容
HashSet的底层是HashMap,
HashMap的原理
HashMap的创建
在jdk1.8以前:在构造方法中创建一个长度是16的Entry[]table的数组来存储数据。
在jdk1.8以后:在第一次调用put方法时创建一个长度16的数组Node[]table来存储数据
HashMap的添加
1.计算出在数组的位置。根据key计算hashCode值,用这个值和数组的长度-1进行与运算,得出在数组的索引。
2.如果在数组上这个位置没有数据,就将元素放到这个位置上,
如果这个位置上有数据,将key和这个位置上的所有数据遍历进行equals,如果都不相等,就将数据放到链表的尾部,如果有相等的,就将value值替换。
线程安全的容器
vectory,hashtable
vectory中的方法基本上都加了synchronized关键字实现线程安全,其他和ArrayList没有区别
hashtable的所有涉及到并发的方法的被synchroniced关键字修饰(即在方法加了同步锁),所以在并发情况下是线程安全的,但又因为被sysnchronized修饰所以性能没有hashmap好。
HashTable是通过同步锁线程安全的(和vector一样),每次扩容都是旧数组大小的两倍,如果超出最大容量则使用最大容量来进行扩容,在添加,修改、删除方面速度比较快,在查询方面需要进行遍历,此外,要尽量避免重新散列,因为重新散列会对性能有所影响。
Concurrent包中的类
ConcurrentHashMap
数组+链表改为数组+链表+红黑树
Put 操作的变化
1、根据 key 计算出 hashcode,然后开始遍历 table;
2、判断是否需要初始化;
3、f 即为当前 key 定位出的 Node,如果为空表示当前位置可以写入数据,利用 CAS 尝试写入,失败则自旋保证成功。
4、如果当前位置的 hashcode == MOVED == -1,则需要进行扩容。
5、如果都不满足,则利用 synchronized 锁写入数据。
7、如果数量大于 TREEIFY_THRESHOLD 则要转换为红黑树。
Get 操作的变化
根据计算出来的 hashcode 寻址,如果就在桶上那么直接返回值。
如果是红黑树那就按照树的方式获取值。
都不满足那就按照链表的方式遍历获取值。