一、简介
List 与 Set 是两个常用的容器接口,其中List的特点是有序,Set的特点是元素唯一。
常用的List实现有ArrayList、LinkedList、Vector。
常用的Set实现有HashSet、LinkedHashSet、TreeSet。
二、List实现
1、ArrayList:
ArrayList底层由数组实现,因此查询效率很高,而对于插入删除操作,需要将插入、删除的元素后的所有元素右移或前移,所以在数据插入删除操作频繁时不建议使用,查询情况较多的情况下ArrayList是个很好的选择。
2、LinkedList:
LinkedList底层由双端链表实现,对于插入、删除操作,仅需要改变前一个节点和后一个节点的指向,即可完成插入、删除操作,而对于查询操作,链表需要从头节点或尾节点开始遍历,所以查询效率低下。
3、Vector:
Vector实现与ArrayList类似,但大多数公共方法由synchronized
修饰,所以是线程安全的,但也因此效率较低,所以在没有线程安全要求的情况下,尽量不使用该实现。另外Vector与ArrayList在新增时会有扩容操作,ArrayList默认每次增长50%,Vector增长100%。
4、ArrayList扩容实现:
public boolean add(E e) {
//添加操作前的扩容操作
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
//扩容函数
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//计算最小扩容数
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//使用无参构造器时,默认增长因子是10,所以在长度较小时,默认扩容至10(使用其他构造器则不使用默认的增长因子)
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 判断是否需要扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//新的数组长度等于原数组的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//传入的容器长度小于原数组的1.5倍,则扩容至1.5倍
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//容器极限长度的处理
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 将原数组复制到新数组中
elementData = Arrays.copyOf(elementData, newCapacity);
}
//大于默认的容器最大长度是,将长度置为Integer最大值
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
可以看到,扩容在前期长度较小时,是直接扩容到10的,避免前期的频繁扩容操作。
其中每次扩容50%体现在int newCapacity = oldCapacity + (oldCapacity >> 1);
右移操作就等于除以2。
5、Vector扩容实现:
public synchronized void addElement(E obj) {
modCount++;
//扩容
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = obj;
}
private void ensureCapacityHelper(int minCapacity) {
// 判断是否需要扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//扩容为原长度2倍
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
//容器极限长度的处理,与ArrayList相同
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
三、Set简介:
1、HashSet:
底层通过HashMap实现,无序,因为将HashMap的键作为Set的值,所以可以防止出现重复值。
2、LinkedHashSet:
底层通过LinkedHash实现,通过链表方式可以实现有序的Set,值得一提的是,LinkedHashSet中仅有几个构造函数,具体实现实际是继承了HashSet,在HashSet中有提供包访问权限的构造函数,使用的是LinkedHashMap。
3、TreeSet:
底层通过TreeMap实现,其中提供的排序的方法,这里的排序与LinkedHashSet的有序是不一样的,这里的排序是指通过存入元素的数值大小进行排序(实现Comparable接口),有序是指保持了插入顺序,遍历时是按插入顺序便利的。