List
List 是 Collection 的一个子接口,它拥有 Collection 中的所有方法,由于具有索引,所以可以进行一些索引操作
常用 API
方法 | 说明 |
---|---|
boolean add(int index, E e) | 添加元素至指定索引 |
boolean addAll(Collection c) | |
boolean addAll(int index, Collection c) | 添加集合至指定索引 |
Object get(int index) | 获取指定索引位置元素 |
int indexOf(Object o) | 获取元素第一次出现的索引 |
int lastIndexOf(Object o) | 获取元素最后一次出现的索引 |
Object remove(int index) | 删除指定索引位置元素,返回删除值 |
Object set(int index, E element) | 修改指定索引位置元素值,返回原值 |
List<E> subList(int fromIndex, int toIndex) | 获取索引范围内的元素,左开右闭 |
boolean equals(Object o) | 将指定的对象与此列表进行比较以获得相等性 |
ListIterator<E> listIterator() | 返回列表中的列表迭代器(按适当的顺序) |
ListIterator<E> listIterator(int index) | 从指定位置开始,返回列表的列表迭代器 |
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("1");
// 在指定索引处添加元素
list.add(1, "3");
// 删除指定索引处元素并返回
String remove = list.remove(1);
// 修改指定索引处元素并返回原值
String set = list.set(1, "3");
// 获取指定索引处元素
String s1 = list.get(1);
for (String s : list) {
System.out.println(s);
}
}
遍历
除了使用 Collection 中的两种遍历之外,List 可以使用独特的遍历方式
- 根据索引进行遍历
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
- 通过 ListIterator 迭代器遍历
列表迭代器除了可以往后遍历,还允许我们往前遍历
- previous:返回上一个元素
- hasPrevious:判断是否可以往前迭代
- add(E e):插入元素
与 Iterator 接口相比,该接口增加了向前迭代的功能,还可以通过 add 方法添加元素
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
// 获取 列表迭代器
ListIterator<String> stringListIterator = list.listIterator();
while (stringListIterator.hasNext()) {
String next = stringListIterator.next();
System.out.println(next);
}
}
自定义排序
由于 List 拥有索引,所以我们可以对其进行自定义规则的排序,如使用冒泡排序:
public static void bubbleSort(List list) {
int size = list.size();
for (int i = 0; i < size - 1; i++) {
for (int j = 0; j < size - i - 1; j ++) {
Book book1 = (Book)list.get(j);
Book book2 = (Book)list.get(j + 1);
if (book1.getPrice() > book2.getPrice()) {
list.set(j, book2);
list.set(j + 1, book1);
}
}
}
}
ArrayList
概述
ArrayList 作为 List 的一个实现类,其有 (1) 元素有序、可重复、支持索引操作 等 List 接口的特点
除了 List 的几个特点之外,ArrayList 还允许 (2) 存储 null 值,且支持多个
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add(null);
list.add(null);
list.forEach(System.out::println);
}
null
null
在并发下,(3) ArrayList 是不安全的
常用API
除了拥有 List 的所有方法,还新增了一些方法
方法 | 说明 |
---|---|
Object clone() | 返回此 ArrayList 实例的浅拷贝 |
void ensureCapacity(int minCapacity) | 扩容 |
void foreach(Consumer<? super E> action) | 对每个元素执行特定操作 |
int lastIndexObf(Object o) | |
protected void removeRange(int fromIndex, int toIndex) | |
List<E> subList(int formIndex, int toIndex) | 返回一个子序列 |
void trimToSize() | 将列表容量调为当前大小 |
创建
ArrayList 的底层使用的是一个 可扩容的动态数组
transient Object[] elementData; // non-private to simplify nested class access
transient:瞬时的、短暂的,表示该属性不会被序列化
当调用无参构造创建 ArrayList 时,会对该数组进行初始化,默认为空数组,此时数组大小为0
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
// private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
}
扩容机制
当第一次使用 add 方法添加元素时,会对数组的大小进行扩容,此时数组大小为 10
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
//ensureCapacityInternal:确认有容量能够添加
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}
// 判断是否为空数组,如果是空数组,则返回默认容量和当前最少需要容量的最大值
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
// 如果不是空数组,则返回目前最少需要的容量
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
// 判断当前最少需要容量是否以及超过数组的大小
// 如果超过了,则需要进行扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
- DEFAULT_CAPACITY = 10:默认容量大小为 10
实际进行扩容的方法:grow
private void grow(int minCapacity) {
// 传进一个最小需要的容量大小
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果扩容一次后的容量还是不够实际需要的容量,则扩容到实际需要的容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果扩容后的容量超过了最大允许值,则进行大扩容
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 扩容后,使用copyOf函数复制数组内容,并用null填充多余部分
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
扩容时,每次扩容的容量会是原来的 1.5 倍
如果创建数组的时候,使用的是有参构造,即传入了初始时容器的大小,则创建指定大小的数组;当需要扩容时,还是按照原来容量的 1.5 倍进行扩容
与 LinkedList 的区别
底层结构不同:ArrayList 底层为一个可变数组;LinkedList 底层为双向链表
效率的不同:ArrayList 改查快,增删慢;LinkedList 改查慢,增删快
内存结构的不同:ArrayList 必须开辟连续空间;LinkedList 无需开辟连续空间
与 Vector 的区别
相同点:两者的底层结构都是一个可变数组,默认大小都是 10
不同点:
- 版本的不同:ArrayList 自 JDK 1.2 引入;Vector 自 JDK 1.0 引入
- 效率差异:ArrayList 是线程不安全集合类,但其效率比 Vector 高;而 Vector 是线程安全集合类
- 扩容倍数的不同:ArrayList 进行扩容时,按照 1.5 倍进行扩容,而 Vector 按照原来的 2 倍进行扩容
LinkedList
概述
LinkedList 实现了 List、Queue 接口
底层采用 双向链表,拥有 (1)增删快、查询慢 的特点
(2) LinkedList 是线程不安全的,其内部没有实现同步
由于 LinkedList 的底层是一个双向链表,所以可以对它进行一些首位操作
常用 API
LinkedList 除了拥有 List 所有的方法外,还可以通过一些特有的 API 对链表首尾进行一些操作
方法 | 说明 |
---|---|
void addFirst(E e) | 在该列表开头插入指定的元素 |
void addLast(E e) | 将指定的元素追加到此列表的末尾 |
E element() | 检索但不删除此列表的头 |
E getFirst() | 返回此列表中的第一个元素 |
E getLast() | 返回此列表中的最后一个元素 |
int lastIndexOf(Object o) | |
boolean offer(E e) | 将指定的元素添加为此列表的尾部 |
boolean offerFirst(E e) | 在此列表的前面插入指定的元素 |
boolean offerLast(E e) | 在该列表的末尾插入指定的元素 |
E peek() | 检索但不删除此列表的头 |
E peekFirst() | 检索但不删除此列表的第一个元素,空返回null |
E peekLast() | 检索但不删除此列表的最后一个元素 |
E poll() | 检索并删除此列表的头 |
E pollFirst() | |
E pollLast() | |
E pop() | 从此列表表示的堆栈中弹出一个元素 |
void push(E e) | 将元素推送到由此列表表示的堆栈上 |
E removeFirst() | 从此列表中删除并返回第一个元素 |
boolean removeFirstOccurence(Object o) | 删除指定元素第一个出现的 |
E removeLast() | |
boolean removeLastOccurence(Object o) |
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("1");
list.add("2");
list.addFirst("0"); // 在链表头添加元素
list.addLast("4"); // 在链表尾添加元素
String first = list.getFirst(); // 获取表头的元素
String last = list.getLast(); // 获取表尾的元素
String s = list.removeFirst(); // 删除表头的元素
String s1 = list.removeLast(); // 删除表尾的元素
// 清空链表
while (!list.isEmpty()) {
list.pop();
}
}
底层结构
LinkedList 在底层维护了一个 Node 节点
private static class Node<E> {
E item; // 存放数据
Node<E> next; // 指向下一个节点
Node<E> prev; // 指向前一个节点
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
并且维护了两个字段,用来指向首节点和尾节点
transient Node<E> first;
transient Node<E> last;
add 方法
add 方法:把元素加到队列最后
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
Vector
Vector 是 JDK 1.0 引入的一个 (1)线程安全 的集合类。其操作与 ArrayList 大致相同
其 (2)首次扩容的默认大小是10,元素有序、可重复、支持索引操作。底层的所有操作都带有 synchronized
在进行扩容的时候会 (3)以2倍进行扩容
特有 API:
方法 | 说明 |
---|---|
int capacity () | 返回此向量的容量 |
void copyInto (Object[] array) | 将此向量复制到指定数组中 |
Enumeration<E> elements () | 返回此向量的枚举 |
Stack
Stack:栈,继承自 Vector,底层为可扩容的数组
特性:先进后出(FILO,First In Last Out)
使用空参构造创建栈时,会创建一个空堆栈
栈包含了 Vector 中的全部API,也有一些特有 API
方法 | 说明 |
---|---|
boolean empty() | 判断此堆栈是否为空 |
E peek() | 获取栈顶元素 |
E pop() | 删除栈顶元素并返回 |
E push(E item) | 进栈操作 |
int search(Object o) | 查找元素o在栈中的位置,由栈底向栈顶方向数 |
public static void main(String[] args) {
Stack<Integer> stack = new Stack<Integer>();
for (int i = 0; i < 5; i++) {
stack.push(i);
}
for (Integer integer : stack) {
System.out.println(integer);
}
// 查找 2 在栈中的位置,并输出,下标自1开始
int pos = stack.search(2);
stack.pop();
Integer peek = stack.peek();
}