前言
jdk 版本 jdk1.8.0_161
集合实现:
通用实现:为日常使用而设计的
Interfaces | Hash table Implementations | Resizable array Implementations | Tree Implementations | Linked list Implementations | Hash table + Linked list Implementations |
---|---|---|---|---|---|
Set | HashSet | TreeSet | LinkedHashSet | ||
List | ArrayList | LinkedList | |||
Queue | |||||
Deque | ArrayDeque | LinkedList | |||
Map | HashMap | TreeMap | LinkedHashMap |
概览
相关类接口 UML 图(idea 生成)
说明:
Iterable : 始于 1.5版本,实现该接口的类 可以使用 for-each 循环语句完成遍历。持有 迭代器对象 Iterator;
Collection: 基类;
AbstractCollection: Collection 接口的一个骨架实现,目的是减少后续实现类实现抽象方法的数量;
List: 有序的集合(也被称为一个序列(sequence),有序是指插入取出有序),使用该接口可以精确的控制列表中每个元素的插入位置,可以通过索引快速访问、查询元素。list 允许重复元素,允许多个null 元素;
RandomAccess:标记接口(marker interface),用来表示 List 实现类 支持 快速随机访问。这个接口的目的是当 随机 或者 顺序 获取 list 时,允许 泛型算法 来改变 他们的行为 以 提供 好的性能;
AbstractList:List 接口的一个骨架实现;对于需要 随机存取的 数据类型需要继承该类,比如 ArrayList 的数组,该抽象类对 hashcode 方法 和 equals 方法 进行了重写
Cloneable:实现该接口 表示 ,对象支持 克隆,否则会抛出异常;
Serializable:实现该接口的类表示 开启 类的序列化;
ArrayList :
List 接口 可变数组的实现,和 Vector基本相同, 区别在于 ArrayList 是 非同步的,Vector 是 同步的 (线程安全的);
特点:有序的(元素的插入顺序有序);可以保存 null 值;允许重复值;
size,isEmpty,get,set,iterator,listIterator 操作花费是 常量时间;
add 操作 以 摊还常量时间( amortized constant time) 运行,即 添加 n个元素的时间花费 为 O(n);
其他操作是线性时间运行(粗略来说),常量因子比 LinkedList 的低;
每个 ArrayList 实例 有一个 capacity, capacity 是 存储 元素的数组的大小。默认为 10,当元素 被添加到 ArrayList 中,他的 capacity 会自动增长。
该实现类是非同步的,如果在多线程下使用,可以使用 Collections.synchronizedList() 静态方法实现。
通过 Iterator 类和 ListIterator 方法 返回的 iterators 是 快速失败(fail-fast):如果 在 iterator 被创建后,list 结构上被修改,这种修改不是通过 iterator 自己的 remove 和 add 方法实现的,那么 iterator 会抛出 一个 ConcurrentModificationException;如下示例将抛出异常:
@Test
public void failFastTest() {
List<String> names = this.createList();
for (Iterator iterator = names.iterator();iterator.hasNext();) {
// iterator.next();
// iterator.remove();
names.remove(iterator.next());
}
// LOGGER.debug("names size is {}", names.size()); // 0
}
private List<String> createList() {
List<String> names = new ArrayList<>();
names.add("小明");
names.add("小强");
names.add("小红");
names.add("小张");
return names;
}
ArrayList 源码 解析
构造函数
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;
/**
* 构造一个空 list,当需要添加元素时,初始化容量,最小为10
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 通过给定的初始容量值构造一个 空
list
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
/**
* 构造一个包含指定 collection 中元素 的 list, 顺序为 collection 对应 迭代器返回的顺序
*
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
增删改查方法
增加单个元素(添加多个元素 为 size + numNew):添加在尾部,时间复杂度为 O(1)
public boolean add(E e) {
ensureCapacityInternal(size + 1); //处理内部容量,size 为 容器中存储的元素个数,此时总元素个数为 size 加 1
elementData[size++] = e;//先在容器的 size 处添加元素,然后 size 加 1
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// 计算容量,是否为默认容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {// == 比较地址,是否为默认容量
return Math.max(DEFAULT_CAPACITY, minCapacity);//取默认容量 和 传入 容量值中最大的一个
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;//AbstractList 中定义的字段,记录 list 结构上修改的次数,给迭代器使用
// 超出 数组长度,扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;//原始容量
int newCapacity = oldCapacity + (oldCapacity >> 1);//扩容后的容量,左移一位除以2,扩容 1.5倍
if (newCapacity - minCapacity < 0)//扩容后的容量仍然不能满足需求
newCapacity = minCapacity;//直接使用要求的容量
if (newCapacity - MAX_ARRAY_SIZE > 0)//扩容后的容量超出数组最大界定容量
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);//构造新数组
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
//三目运算符,请求容量 大于数组最大可分配容量的话,返回 整型最大值,
//否则返回数组的可分配最大容量
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
/**
* 数组可以分配的最大容量
* 一些 虚拟机会在数组中保留 一些 头信息
*如果尝试分配更大容量,则会导致
* OutOfMemoryError:请求数组大小超出 VM 限制
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
在指定索引处增加元素:涉及到索引,需要移动后续元素,相对费时。时间复杂度 O(n)
/**
* 在list 的指定位置插入指定元素
* 将当前在该位置的元素(如果有的话)和任何后续元素向右移动
*
*/
public void add(int index, E element) {
rangeCheckForAdd(index); // 检查角标是否合法
ensureCapacityInternal(size + 1); //容量确认,是否需要扩容
// 调用本地方法:完成数组中数据后移
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element; //插入新元素
size++; //元素数量 + 1
}
删除某个元素:因为元素是可重复,所以只移除第一次出现的元素,涉及到元素移动,相对耗时,时间复杂度 O(n)
/**
* 移除指定元素在 list 中第一次出现的元素
* 即移除 指定元素的 最小索引元素
*/
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
/*
* 跳过边界检查 并且不返回被移除的值
*
*/
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1; // 需要移动的数据数量
if (numMoved > 0)
// 将移除元素之后的所有元素向左移动
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; //垃圾回收
}
删除除指定角标的元素:相对于移除指定元素,省去了元素查找,仍然需要移动元素,时间复杂度 O(n)
/**
* 移除 list 中指定位置的元素
* 左移后续元素
*/
public E remove(int index) {
rangeCheck(index);//检验角标
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;// 被移动元素的个数
if (numMoved > 0)
// 元素移动
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // 垃圾回收
return oldValue;
}
修改指定位置的元素:时间复杂度 O(1)
/**
*使用 指定元素 替换指定位置的元素
*/
public E set(int index, E element) {
rangeCheck(index);//角标检查
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
查询指定位置的元素:时间复杂度 O(1)
public E get(int index) {
rangeCheck(index); //角标检查
return elementData(index);
}
函数式接口相关方法
因为函数式接口的出现,jdk 1.8 新增了不少 方法:
forEach 方法:可以直接遍历 集合;重写 Iterable 接口中的默认方法;使用 Consumer 函数式接口
Iterable 接口中的默认方法:
default void forEach(Consumer<? super T> action) { // Consumer 接收一个参数,不返回任何值函数式接口
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t); //调用 Consumer 中的方法
}
}
ArrayList 类中重写的方法:
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int expectedModCount = modCount;
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) this.elementData;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) { //最原始的循环方法
action.accept(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
spliterator 方法:Spliterator 是一个用于 遍历 和 分隔 源数据 的函数式接口 ;
@Override
public Spliterator<E> spliterator() {
return new ArrayListSpliterator<>(this, 0, -1, 0);
}
removeIf 方法:移除符合条件的 元素;使用 Predicate 函数式接口
@Override
public boolean removeIf(Predicate<? super E> filter) { // Predicate 断言一个 参数值 的 函数式接口,返回 true or false
Objects.requireNonNull(filter);
// figure out which elements are to be removed
// any exception thrown from the filter predicate at this stage
// will leave the collection unmodified
int removeCount = 0;
final BitSet removeSet = new BitSet(size);
final int expectedModCount = modCount;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
@SuppressWarnings("unchecked")
final E element = (E) elementData[i];
if (filter.test(element)) {
removeSet.set(i);
removeCount++;
}
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
// shift surviving elements left over the spaces left by removed elements
final boolean anyToRemove = removeCount > 0;
if (anyToRemove) {
final int newSize = size - removeCount;
for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
i = removeSet.nextClearBit(i);
elementData[j] = elementData[i];
}
for (int k=newSize; k < size; k++) {
elementData[k] = null; // Let gc do its work
}
this.size = newSize;
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
return anyToRemove;
}
replaceAll 方法: 替换指定的元素;使用 UnaryOperator 函数式接口
@Override
@SuppressWarnings("unchecked")
public void replaceAll(UnaryOperator<E> operator) {// UnaryOperator 对单个数的操作,返回和操作数类型相同的结果
Objects.requireNonNull(operator);
final int expectedModCount = modCount;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
elementData[i] = operator.apply((E) elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
sort 方法: 排序;使用 Comparator 函数式接口,
@Override
@SuppressWarnings("unchecked")
public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
代码展示:
@Test
public void methodTest() {
List<String> fruits = this.createFruit();
//forEach方法 的 lambda
fruits.forEach(fruit -> {
System.out.println(fruit);
});
//forEach方法 的方法引用
fruits.forEach(System.out::println);
// removeIf 方法
fruits.removeIf(s -> s.equals("orange"));
// replaceAll 方法
fruits.replaceAll(s -> {
if ("apple".equals(s)) {
return "watermelon";
}
return s;
});
// 避免 lambda 体的 写法
fruits.replaceAll(this::unaryOperator);
System.out.println(fruits);
}
private String unaryOperator(String s) {
if ("apple".equals(s)) {
return "watermelon";
}
return s;
}
public List<String> createFruit() {
List<String> fruits = new ArrayList<>();
fruits.add("apple");
fruits.add("orange");
return fruits;
}