介绍Java容器原理、特性及简单使用方式,主要包含Collection接口及其主要实现类。
Java集合系列文章包括:
本文主要介绍Java容器原理、特性及简单使用方式,主要包含Collection接口及其主要实现类。
1. 概览
Java 集合, 也叫作容器,主要是由两大接口派生而来:一个是 Collecton接口,主要用于存放单一元素;另一个是 Map 接口,主要用于存放键值对。对于Collection 接口,下面又有三个主要的子接口:List、Set 和 Queue。
2. Collection接口
3. 常用实现类
- ArrayList
- List接口的主要实现类,利用数组存储数据,它能够添加null元素,使用数组下标查找数据O(1),适用于频繁的查找工作
- 插入元素时,当size == elementData.length的时候,就需要扩容。初始化的时候尽量指定容量,减少扩容次数
- 线程不安全
- LinkedList
- Vector
- Stack
3. ArrayList
1. 数据结构
数组
/**
* 存储元素的数组,当size等于数组的长度,再次进行插入就需要扩容
*/
transient Object[] elementData;
/**
* 存储的元素的个数,一般情况下,size != elementData.length
*/
private int size;
2. 插入元素(add)
1. 扩容
插入元素时,当size == elementData.length
的时候,就需要扩容。扩容实际上是通过System.arraycopy
方法将旧数组内容复制到新数组,新数组长度的大小逻辑如下:
- 初始情况下,数组长度扩容为10(默认的)
- 其他情况下,新数组的长度是原数组的1.5倍(oldCapacity + (oldCapacity >> 1)),数组长度最大为Integer.MAX_VALUE
2. 两种插入方式
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length) {
elementData = grow(); // 扩容
}
elementData[s] = e;
size = s + 1;
}
/**
* 元素插入到数组末尾
*/
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
/**
* 在指定位置插入元素
*/
public void add(int index, E element) {
rangeCheckForAdd(index);
modCount++;
final int s;
Object[] elementData;
if ((s = size) == (elementData = this.elementData).length) {
elementData = grow();
}
System.arraycopy(elementData, index, elementData, index + 1, s - index);
elementData[index] = element;
size = s + 1;
}
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
- 元素插入到数组末尾。首先判断是否需要扩容,然后给elementData[size]赋值,时间复杂度为O(1)
- 元素插入到指定位置。首先判断指定位置是否越界了([0, size]),然后判断是否需要扩容,然后将[index, size - 1]的元素移动到[index + 1, size],这里是通过System.arraycopy直接复制指定区间的元素,速度比循环更快,最后给elementData[index]赋值
- ArrayList还提供了批量插入元素的方法(addAll)
因此,在中间位置插入元素效率比在末尾插入要低,所以我们在使用的时候,尽量不要在中间位置插入元素。
3. 访问元素(get)
public E get(int index) {
Objects.checkIndex(index, size);
return elementData(index);
}
E elementData(int index) {
return (E) elementData[index];
}
- 通过数组下标访问元素,时间复杂度为O(1)
- ArrayList还提供了判断集合是否包含元素(contains)和查询元素在集合中的下标(indexOf)的方法
4. 删除元素(remove)
/**
* 删除指定位置元素
*/
public E remove(int index) {
Objects.checkIndex(index, size);
final Object[] es = elementData;
("unchecked") E oldValue = (E) es[index];
fastRemove(es, index);
return oldValue;
}
/**
* 删除指定元素
*/
public boolean remove(Object o) {
final Object[] es = elementData;
final int size = this.size;
int i = 0;
found: {
if (o == null) {
for (; i < size; i++) {
if (es[i] == null) {
break found;
}
}
} else {
for (; i < size; i++) {
if (o.equals(es[i])) {
break found;
}
}
}
return false;
}
fastRemove(es, i);
return true;
}
private void fastRemove(Object[] es, int i) {
modCount++;
final int newSize;
if ((newSize = size - 1) > i) {
System.arraycopy(es, i + 1, es, i, newSize - i);
}
es[size = newSize] = null;
}
- 删除指定位置元素:将指定元素后面的元素整个向前挪一个位置,再将最后一个元素置为null
- 删除指定元素:定位指定元素下标,之后的操作与删除指定位置元素一致
- ArrayList还提供了删除符合条件元素的方法(removeIf)
4. LinkedList
5. Vector
6. Stack
7. 参考
-