简答题
提示:使用CSDN右侧(网页版)的“目录”功能阅读体验更佳
Java容器
Java集合类继承关系
1.请你说明HashMap和Hashtable的区别?
HashMap和Hashtable都实现了Map接口,因此有很多相似点,但是也有以下不同点:
- HashMap允许键和值为空值(但只能有一个键为空值),Hashtable不允许空值作为键或值
- HashMap是线程不安全的,而HashTable的方法基本都使用synchronized修饰了,因此在多线程环境下Hashtable是线程安全的。
- 因为Hashtable是同步的,所以在效率上要低于HashMap,因此被认为是一个一般不使用的遗留类(如果需要保证线程安全一般使用JUC的ConcurrentHashMap类)
- 在未给定容量时,HashMap的默认初始容量是16,之后每次扩容会变为原来的2倍;Hashtable默认大小是11,之后每次扩容会变为原来的2n+1;在指定初始容量时,Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为2的幂次方大小(HashMap 中的
tableSizeFor()
方法保证)。也就是说 HashMap 总是使用2的幂作为哈希表的大小。 - JDK1.8后的HashMap在解决哈希冲突时有了变化,当链表长度大于阈值(默认8)时,会将链表转化为红黑树,以减少搜索时间,而Hashtable没有这样的机制。
2.请你说明HashMap 和 HashSet区别?
- HashMap实现了Map接口,而HashSet实现的是Set接口
- HashMap以键值对的形式存储对象,HashSet仅存储对象
- HashMap使用键(key)计算Hashcode,HashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性
3.ArrayList
ArrayList底层使用动态数组存储数据,初始容量为10,且支持快速随机访问(实现RandomAccess)
虽然ArrayList的初始容量是10,但在以无参构造方法创建ArrayList的时候新建的是一个空数组,只有等到添加元素时才会分配容量。即在添加第一个元素后容量分配为10。
ArrayList的扩容底层是由复制数组实现的,操作代价较高,最好在创建时指定足够的容量。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
private static final int DEFAULT_CAPACITY = 10;
ArrayList的自动扩容
ArrayList的扩容发生在添加元素时,依次涉及到5个方法。
1.add()方法
添加元素前将当前元素数+1作为参数调用ensureCapacityInternal()方法。
public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
2.ensureCapacityInternal()方法
得到最小扩容量
当添加第一个元素时,可知参数minCapacity传入时为1,在if语句执行后为10。
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// 获取默认的容量和传入参数的较大值
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
3.ensureExplicitCapacity()方法
判断是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
//调用grow方法进行扩容,调用此方法代表已经开始扩容了
grow(minCapacity);
}
4.grow()方法
ArrayList扩容的核心方法,在确定了新容量后通过Arrays.copyOf()方法将原数组复制到新数组里
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
//旧的容量
int oldCapacity = elementData.length;
//扩容为旧容量的1.5倍(JDK1.8),在JDK1.6以前为旧容量的1.5倍+1;
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果扩容后还是小于最小所需容量,则将新容量设为最小所需容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;