文章目录
1.集合框架
所有集合实现类的最顶层接口为Iterable和Collection接口,再向下Collection分为了三种不同的形式,分别是List,Queue和Set接口,然后就是对应的不同的实现方式。
1.1 顶层接口Iterable
package java.lang;
import java.util.Iterator;
import java.util.Objects;
import java.util.Spliterator;
import java.util.Spliterators;
//支持lambda函数接口
import java.util.function.Consumer;
public interface Iterable<T> {
//iterator()方法
Iterator<T> iterator();
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}
Iterable接口中只有iterator()一个接口方法,Iterator也是一个接口,其主要有如下两个方法hasNext()和next()方法。
boolean hasNext();
E next();
1.2 Collection接口
package java.util;
import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public interface Collection<E> extends Iterable<E> {
int size();
boolean isEmpty();
boolean contains(Object o);
Iterator<E> iterator();
Object[] toArray();
boolean add(E e);
boolean remove(Object o);
boolean containsAll(Collection<?> c);
boolean removeAll(Collection<?> c);
default boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
boolean removed = false;
final Iterator<E> each = iterator();
while (each.hasNext()) {
if (filter.test(each.next())) {
each.remove();
removed = true;
}
}
return removed;
}
boolean retainAll(Collection<?> c);
void clear();
int hashCode();
@Override
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, 0);
}
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
}
Collection的主要接口方法有:
int size(); 集合大小
boolean isEmpty() 集合是否为空
boolean contains(Object o) 是否包含某元素
Iterator iterator() 迭代
Object[] toArray() 转数组
boolean add(E e) 添加元素
boolean remove(Object o) 移除某元素
boolean containsAll(Collection<?> c) 是否包含另一个集合
boolean removeAll(Collection<?> c) 移除集合中所有在集合c中的元素
boolean retainAll(Collection<?> c)) 判断集合中不在集合c中的元素
void clear() 清空所有元素
2.List
List接口对Collection的扩展:多了添加方法add和addAll查找方法get,indexOf,set等方法,并且支持index下标操作
2.1 List接口
Collection与List的区别?
a. Collection和List最大的区别就是Collection是无序的,不支持索引操作,而List是有序的。Collection没有顺序的概念。
b. List中Iterator为ListIterator。
c. 由a推导List可以进行排序,所以List接口支持使用sort方法。
d. 二者的Spliterator操作方式不一样。
2.2 List的实现类ArrayList
ArrayList是List接口最常用的一个实现类,支持List接口的一些列操作。
2.2.1 ArrayList继承关系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AAjBIlGu-1678588100627)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20210508095712036.png)]
2.2.2 ArrayList组成
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;
ArryList的 transient Object[] elementData; 是ArrayList保存数据的容器,ArrayList是由数组进行实现的。
2.2.3 ArrayList的构造方法
/**
* Default initial capacity.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
private static final Object[] EMPTY_ELEMENTDATA = {};
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);
}
}
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
ArrayList支持有参和无参的构造方法:
- 无参构造,创建的是空集合
- 有参构造,根据传入的参数进行创建,支持空集合创建。
2.2.4 ArryList的add方法
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
ensureCapcityInternal 方法主要是做了这几件事情:
-
增加modCount的数值,modCount代表集合操作的次数。
-
判断集合长度是否超过集合的长度,是扩容。
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
ensureExplicitCapacity 方法,集合修改次数累加,判断是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
2.2.5 ArrayList的扩容
calculateCaoacity方法中
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
在无参构造方法中,知道elementData 指向的是DEFAULTCAPACITY_EMPTY_ELEMENTDATA 空数组,会直接扩容到DEFAULT_CAPACITY,DEFAULT_CAPACITY的长度为10,而当添加进ArrayList中的元素超过了数组能存放的最大值就会进行扩容。
在grow中可以得知,每次扩容但是1.5倍
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);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
2.2.6 为什么elementData用transient修饰?
- transient的作用是该属性不参与序列化。
- ArrayList继承了标示序列化的Serializable接口。
- 对arrayList序列化的过程中进行了读写安全控制。
2.3 LinkedList
LinkedList是双向链表
2.3.1 LinkedList的继承关系
LinkedList既是List接口的实现也是Queue的实现(Deque),和ArrayList相比LinkedList支持的功能更多,可视作队列来使用
2.3.2 LinkedList结构
LinkedList由一个头节点和一个尾节点组成,分别指向链表的头部和尾部。
transient int size = 0;
transient Node<E> first;
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last;
Node类,由当前值item,和指向上一个节点prev和指向下个节点next的指针组成。并且只含有一个构造方法,按照(prev, item, next)这样的参数顺序构造。
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;
}
}
数据结构中链表的头插法linkFirst和尾插法linkLast
/**
* Links e as first element. 头插法
*/
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
/**
* Links e as last element. 尾插法
*/
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++;
}
node(index)方法的实现
判断index是更靠近头部还是尾部,靠近哪段从哪段遍历获取值。
Node<E> node(int index) {
// assert isElementIndex(index);
//判断index更靠近头部还是尾部
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
查询索引修改方法,先找到对应节点,将新的值替换掉老的值。
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
LinkedList要遍历找到该位置才能进行修改,而ArrayList是内部数组操作会更快
2.3.3 LinkedList修改方法
新增一个节点,采用尾插法将新节点放入尾部。
public boolean add(E e) {
linkLast(e);
return true;
}
2.4 Vector
vactor组成和ArrayList基本一样
2.4.1 vector组成
//存放元素的数组
protected Object[] elementData;
//有效元素数量,小于等于数组长度
protected int elementCount;
//容量增加量,和扩容相关
protected int capacityIncrement;
2.4.2 vector线程安全性
vector是线程安全的,synchronized修饰的操作方法。
2.4.3 vector扩容
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//扩容大小
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
扩容当构造没有capacityIncrement时,一次扩容数组变成原来两倍,否则每次增加capacityIncrement
2.5 Stack
Stack也是List接口的实现类之一,和Vector一样
2.5.1 Stack的继承关系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5pmom0Ai-1678588100631)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20210508175035827.png)]
Stack继承于Vector,也是List接口的实现类。Vector是线程安全的,因为其方法都是synchronized修饰的,所以处Stack从父类Vector继承而来的操作也是线程安全的。
2.5.2 Stack 的使用
Stack是栈的实现,主要操作为push入栈和pop出栈,而栈最大的特点就是LIFO(Last In First Out)
Stack<String> strings = new Stack<>();
strings.push("aaa");
strings.push("bbb");
strings.push("ccc");
System.err.println(strings.pop());
2.5.3 Stack方法分析
用的push方法用的是Vector的addElement(E e)方法,该方法是将元素放在集合的尾部,
而pop方法使用的是Vector的removeElementAt(Index x)方法,移除并获取集合的尾部元素,
Stack的操作就是基于线性表的尾部进行操作的
/**
* Stack源码(Jdk8)
*/
public
class Stack<E> extends Vector<E> {
public Stack() {
}
//入栈,使用的是Vector的addElement方法。
public E push(E item) {
addElement(item);
return item;
}
//出栈,找到数组最后一个元素,移除并返回。
public synchronized E pop() {
E obj;
int len = size();
obj = peek();
removeElementAt(len - 1);
return obj;
}
public synchronized E peek() {
int len = size();
if (len == 0)
throw new EmptyStackException();
return elementAt(len - 1);
}
public boolean empty() {
return size() == 0;
}
public synchronized int search(Object o) {
int i = lastIndexOf(o);
if (i >= 0) {
return size() - i;
}
return -1;
}
private static final long serialVersionUID = 1224463164541339165L;
}
3 Queue
queue是一种先进先出的数据结构,也就是first in first out。可以将queue看作一个只可以从某一段放元素进去的一个容器,取元素只能从另一端取
队列并没有规定是从哪一端插入,从哪一段取出
3.1 Deque是什么?
Deque英文全称是Double ended queue,也就是俗称的双端队列。就是说对于这个队列容器,既可以从头部插入也可以从尾部插入,既可以从头部获取,也可以从尾部获取
3.1.1Java中的Queue接口
package java.util;
public interface Queue<E> extends Collection<E> {
//集合中插入元素
boolean add(E e);
//队列中插入元素
boolean offer(E e);
//移除元素,当集合为空,抛出异常
E remove();
//移除队列头部元素并返回,如果为空,返回null
E poll();
//查询集合第一个元素,如果为空,抛出异常
E element();
//查询队列中第一个元素,如果为空,返回null
E peek();
}
队列的操作不会因为队列为空抛出异常,而集合的操作是队列为空抛出异常。
Queue接口方法 | 作用 |
---|---|
boolean offer(E e) | 往队列中插入元素 |
E poll() | 队列中移除元素,并返回该元素 |
E peek() | 获取队列头部元素,但不做修改 |
3.1.2 Deque接口
相比Queue中的方法,方法名多了First和Last,First结尾的方法即从头部进行操作,Last即从尾部进行操作。
package java.util;
public interface Deque<E> extends Queue<E> {
//deque的操作方法
void addFirst(E e);
void addLast(E e);
boolean offerFirst(E e);
boolean offerLast(E e);
E removeFirst();
E removeLast();
E pollFirst();
E pollLast();
E getFirst();
E getLast();
E peekFirst();
E peekLast();
boolean removeFirstOccurrence(Object o);
boolean removeLastOccurrence(Object o);
// *** Queue methods ***
boolean add(E e);
boolean offer(E e);
E remove();
E poll();
E element();
E peek();
}
3.1.3 Queue,Deque的实现类
Java中关于Queue的实现主要是双端队列,Queue的实现有PriorityQueue,Deque在java.util中主要有ArrayDeque和LinkedList两个实现类,
两者一个是基于数组的实现,一个是基于链表的实现,LinkedList是一个双向链表,在此基础之上实现了Deque接口
3.2 PriorityQueue
PriorityQueue是Java中唯一一个Queue接口的直接实现,优先队列,其内部支持按照一定的规则对内部元素进行排序。
3.2.1 PriorityQueue继承关系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eegULCG8-1678588100632)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20210508182139971.png)]
PriorityQueue的继承实现关系中,是Queue的实现类,主要使用方式是队列的基本操作,Queue的基本原理,其核心是FIFO(First In First Out)原理。