本篇文章是关于Collection下的List,Set,Queue等的讲解,Map接口的实现类HashMap,LinkedHashMap,ConcurrentHashMap,HashTable的讲解看下面的这篇文章:
1 Queue
Queue 与 Deque 的区别
Queue
是单端队列,只能从一端插入元素,另一端删除元素,实现上一般遵循 先进先出(FIFO) 规则。
Queue
扩展了 Collection
的接口,根据 因为容量问题而导致操作失败后处理方式的不同 可以分为两类方法: 一种在操作失败后会抛出异常,另一种则会返回特殊值。
Queue 接口 | 抛出异常 | 返回特殊值 |
---|---|---|
插入队尾 | add(E e) | offer(E e) |
删除队首 | remove() | poll() |
查询队首元素 | element() | peek() |
Deque
是双端队列,在队列的两端均可以插入或删除元素。
Deque
扩展了 Queue
的接口, 增加了在队首和队尾进行插入和删除的方法,同样根据失败后处理方式的不同分为两类:
Deque 接口 | 抛出异常 | 返回特殊值 |
---|---|---|
插入队首 | addFirst(E e) | offerFirst(E e) |
插入队尾 | addLast(E e) | offerLast(E e) |
删除队首 | removeFirst() | pollFirst() |
删除队尾 | removeLast() | pollLast() |
查询队首元素 | getFirst() | peekFirst() |
查询队尾元素 | getLast() | peekLast() |
1.1 priorityQueue
PriorityQueue
:Object[]
数组来实现二叉堆
PriorityQueue
是在 JDK1.5 中被引入的, 其与 Queue
的区别在于元素出队顺序是与优先级相关的,即总是优先级最高的元素先出队。
这里列举其相关的一些要点:
PriorityQueue
利用了二叉堆的数据结构来实现的,底层使用可变长的数组来存储数据PriorityQueue
通过堆元素的上浮和下沉,实现了在 O(logn) 的时间复杂度内插入元素和删除堆顶元素。PriorityQueue
是非线程安全的,且不支持存储NULL
和non-comparable
的对象。PriorityQueue
默认是小顶堆,但可以接收一个Comparator
作为构造参数,从而来自定义元素优先级的先后。
1.1.1 类的常量和字段
// 默认初始化容量
private static final int DEFAULT_INITIAL_CAPACITY = 11;
/**
* Priority queue represented as a balanced binary heap: the two
* children of queue[n] are queue[2*n+1] and queue[2*(n+1)]. The
* priority queue is ordered by comparator, or by the elements'
* natural ordering, if comparator is null: For each node n in the
* heap and each descendant d of n, n <= d. The element with the
* lowest value is in queue[0], assuming the queue is nonempty.
* 底层的数据结构,数组
*/
transient Object[] queue; // non-private to simplify nested class access
/**
* The number of elements in the priority queue.
* 元素的数量
*/
private int size = 0;
/**
* The comparator, or null if priority queue uses elements'
* natural ordering.
* 比较器
*/
private final Comparator<? super E> comparator;
/**
* The number of times this priority queue has been
* <i>structurally modified</i>. See AbstractList for gory details.
* 结构修改的次数
*/
transient int modCount = 0; // non-private to simplify nested class access
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
* 数组最大容量
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
```
1.1.2 构造方法
/**
* Creates a {@code PriorityQueue} with the default initial
* capacity (11) that orders its elements according to their
* {@linkplain Comparable natural ordering}.
*/
public PriorityQueue() {
// 默认无参构造
this(DEFAULT_INITIAL_CAPACITY, null);
}
/**
* Creates a {@code PriorityQueue} with the specified initial
* capacity that orders its elements according to their
* {@linkplain Comparable natural ordering}.
*
* @param initialCapacity the initial capacity for this priority queue
* @throws IllegalArgumentException if {@code initialCapacity} is less
* than 1
*/
public PriorityQueue(int initialCapacity) {
this(initialCapacity, null);
}
/**
* Creates a {@code PriorityQueue} with the default initial capacity and
* whose elements are ordered according to the specified comparator.
*
* @param comparator the comparator that will be used to order this
* priority queue. If {@code null}, the {@linkplain Comparable
* natural ordering} of the elements will be used.
* @since 1.8
*/
public PriorityQueue(Comparator<? super E> comparator) {
this(DEFAULT_INITIAL_CAPACITY, comparator);
}
/**
* Creates a {@code PriorityQueue} with the specified initial capacity
* that orders its elements according to the specified comparator.
*
* @param initialCapacity the initial capacity for this priority queue
* @param comparator the comparator that will be used to order this
* priority queue. If {@code null}, the {@linkplain Comparable
* natural ordering} of the elements will be used.
* @throws IllegalArgumentException if {@code initialCapacity} is
* less than 1
*/
public PriorityQueue(int initialCapacity,
Comparator<? super E> comparator) {
// Note: This restriction of at least one is not actually needed,
// but continues for 1.5 compatibility
//指定初识容量和比较器的构造方法
if (initialCapacity < 1)
throw new IllegalArgumentException();
// 直接初始化
this.queue = new Object[initialCapacity];
this.comparator = comparator;
}
/**
* Creates a {@code PriorityQueue} containing the elements in the
* specified collection. If the specified collection is an instance of
* a {@link SortedSet} or is another {@code PriorityQueue}, this
* priority queue will be ordered according to the same ordering.
* Otherwise, this priority queue will be ordered according to the
* {@linkplain Comparable natural ordering} of its elements.
*
* @param c the collection whose elements are to be placed
* into this priority queue
* @throws ClassCastException if elements of the specified collection
* cannot be compared to one another according to the priority
* queue's ordering
* @throws NullPointerException if the specified collection or any
* of its elements are null
*/
@SuppressWarnings("unchecked")
public PriorityQueue(Collection<? extends E> c) {
// 传入一个集合,那么需要获取它的比较器
if (c instanceof SortedSet<?>) {
SortedSet<? extends E> ss = (SortedSet<? extends E>) c;
this.comparator = (Comparator<? super E>) ss.comparator();
initElementsFromCollection(ss);
}
else if (c instanceof PriorityQueue<?>) {
PriorityQueue<? extends E> pq = (PriorityQueue<? extends E>) c;
this.comparator = (Comparator<? super E>) pq.comparator();
initFromPriorityQueue(pq);
}
else {
this.comparator = null;
initFromCollection(c);
}
}
1.1.3 add方法
public boolean add(E e) {
return offer(e);
}
/**
* Inserts the specified element into this priority queue.
*
* @return {@code true} (as specified by {@link Queue#offer})
* @throws ClassCastException if the specified element cannot be
* compared with elements currently in this priority queue
* according to the priority queue's ordering
* @throws NullPointerException if the specified element is null
*/
public boolean offer(E e) {
// 添加的元素不能为 null
if (e == null)
throw new NullPointerException();
// 修改结构 + 1
modCount++;
// 元素存放的索引位置
int i = size;
// 需要扩容
if (i >= queue.length)
grow(i + 1);
size = i + 1;
// 第一次添加,那么直接添加
if (i == 0)
queue[0] = e;
else
// 不是第一次添加,那么需要将添加的元素上浮到适合位置
siftUp(i, e);
return true;
}
1.1.4 siftUp方法
上浮操作
/**
* Inserts item x at position k, maintaining heap invariant by
* promoting x up the tree until it is greater than or equal to
* its parent, or is the root.
*
* To simplify and speed up coercions and comparisons. the
* Comparable and Comparator versions are separated into different
* methods that are otherwise identical. (Similarly for siftDown.)
* 将项x插入位置k,要保持堆不变性
* 将x向上提升,直到它大于或等于它的父结点,或者是根结点。(小根堆)
* @param k the position to fill
* @param x the item to insert
*/
private void siftUp(int k, E x) {
if (comparator != null)
// 使用自己定义的比较器
siftUpUsingComparator(k, x);
else
// 元素x是实现Comparable接口的
siftUpComparable(k, x);
}
/**
* 根据传入的比较器比较
*/
private void siftUpUsingComparator(int k, E x) {
while (k > 0) {
// 父节点的索引: (k-1) / 2;
int parent = (k - 1) >>> 1;
// 父节点
Object e = queue[parent];
if (comparator.compare(x, (E) e) >= 0)
break;
// k = 父节点索引,继续比较
queue[k] = e;
k = parent;
}
queue[k] = x;
}
/**
* 根据对象实现的比较方法比较
*/
private void siftUpComparable(int k, E x) {
// x是一个可比较对象,内部实现了比较方法
Comparable<? super E> key = (Comparable<? super E>) x;
while (k > 0) {
int parent = (k - 1) >>> 1;
Object e = queue[parent];
if (key.compareTo((E) e) >= 0)
break;
queue[k] = e;
k = parent;
}
queue[k] = key;
}
1.1.5 poll方法
/**
* 返回堆顶元素,并删除
*/
public E poll() {
if (size == 0)
return null;
// 元素数量 -1
int s = --size;
modCount++;
// 堆顶元素
E result = (E) queue[0];
// 堆的最后一位元素
E x = (E) queue[s];
// 最后一位元素 = null
queue[s] = null;
// 堆中不止一个元素,那么需要下沉
if (s != 0)
// 将堆的最后一位元素,放到堆顶,然后下沉
siftDown(0, x);
return result;
}
1.1.6 siftDown方法
下沉方法
/**
* Inserts item x at position k, maintaining heap invariant by
* demoting x down the tree repeatedly until it is less than or
* equal to its children or is a leaf.
* 将项x插入位置k,保持堆不变性
* 重复将x降级到树下,直到它小于或等于它的子节点,或者是一个叶节点(小根堆)
* @param k the position to fill
* @param x the item to insert
*/
private void siftDown(int k, E x) {
if (comparator != null)
siftDownUsingComparator(k, x);
else
siftDownComparable(k, x);
}
/**
* 使用Comparator 比较
*/
private void siftDownUsingComparator(int k, E x) {
// half:元素数量的一半
int half = size >>> 1;
// k 小于 元素数量的一半
while (k < half) {
// 左子节点的索引
int child = (k << 1) + 1;
// 左子节点
Object c = queue[child];
// 右子节点的索引
int right = child + 1;
// 右子节点索引合法 并且 右子节点 需要和 左子节点交换
if (right < size &&
comparator.compare((E) c, (E) queue[right]) > 0)
// 要交换的子节点
c = queue[child = right];
// 子节点和x比较
if (comparator.compare(x, (E) c) <= 0)
break;
// 继续下沉
queue[k] = c;
k = child;
}
// 找到了合适的位置
queue[k] = x;
}
private void siftDownComparable(int k, E x) {
Comparable<? super E> key = (Comparable<? super E>)x;
int half = size >>> 1; // loop while a non-leaf
while (k < half) {
int child = (k << 1) + 1; // assume left child is least
Object c = queue[child];
int right = child + 1;
if (right < size &&
((Comparable<? super E>) c).compareTo((E) queue[right]) > 0)
c = queue[child = right];
if (key.compareTo((E) c) <= 0)
break;
queue[k] = c;
k = child;
}
queue[k] = key;
}
1.1.7 grow扩容
/**
* Increases the capacity of the array.
* 扩容方法
* @param minCapacity the desired minimum capacity 需要扩容的最小值
*/
private void grow(int minCapacity) {
// 旧容量
int oldCapacity = queue.length;
// Double size if small; else grow by 50%
// 新容量 = (如果旧的容量 < 64 那么就翻倍,否则增加 50%)
int newCapacity = oldCapacity + ((oldCapacity < 64) ?
(oldCapacity + 2) :
(oldCapacity >> 1));
// overflow-conscious code
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 拷贝旧数组的数据到新数组
queue = Arrays.copyOf(queue, newCapacity);
}
1.2 ArrayDeque
ArrayDeque
:Object[]
数组 + 双指针
ArrayDeque 与 LinkedList 的区别
ArrayDeque
和 LinkedList
都实现了 Deque
接口,两者都具有队列的功能,但两者有什么区别呢?
ArrayDeque
是基于可变长的数组和双指针来实现,而LinkedList
则通过链表来实现。ArrayDeque
不支持存储NULL
数据,但LinkedList
支持。ArrayDeque
是在 JDK1.6 才被引入的,而LinkedList
早在 JDK1.2 时就已经存在。ArrayDeque
插入时可能存在扩容过程, 不过均摊后的插入操作依然为 O(1)。虽然LinkedList
不需要扩容,但是每次插入数据时均需要申请新的堆空间,均摊性能相比更慢。
从性能的角度上,选用 ArrayDeque
来实现队列要比 LinkedList
更好。此外,ArrayDeque
也可以用于实现栈
1.2.1 类的常量和字段
/**
* The array in which the elements of the deque are stored.
* The capacity of the deque is the length of this array, which is
* always a power of two. The array is never allowed to become
* full, except transiently within an addX method where it is
* resized (see doubleCapacity) immediately upon becoming full,
* thus avoiding head and tail wrapping around to equal each
* other. We also guarantee that all array cells not holding
* deque elements are always null.
* 底层数据结构:数组
*/
transient Object[] elements; // non-private to simplify nested class access
/**
* The index of the element at the head of the deque (which is the
* element that would be removed by remove() or pop()); or an
* arbitrary number equal to tail if the deque is empty.
* 头指针
*/
transient int head;
/**
* The index at which the next element would be added to the tail
* of the deque (via addLast(E), add(E), or push(E)).
* 尾指针
*/
transient int tail;
/**
* The minimum capacity that we'll use for a newly created deque.
* Must be a power of 2.
* 新创建数组的最小容量,数组容量必须是 2 的n次幂
*/
private static final int MIN_INITIAL_CAPACITY = 8;
1.2.2 构造方法
/**
* Constructs an empty array deque with an initial capacity
* sufficient to hold 16 elements.
*/
public ArrayDeque() {
// 默认初始容量 16
elements = new Object[16];
}
/**
* Constructs an empty array deque with an initial capacity
* sufficient to hold the specified number of elements.
*
* @param numElements lower bound on initial capacity of the deque
*/
public ArrayDeque(int numElements) {
// 分配数组容量,数组容量必须是2的n次方
allocateElements(numElements);
}
/**
* Constructs a deque containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator. (The first element returned by the collection's
* iterator becomes the first element, or <i>front</i> of the
* deque.)
*
* @param c the collection whose elements are to be placed into the deque
* @throws NullPointerException if the specified collection is null
*/
public ArrayDeque(Collection<? extends E> c) {
// 分配数组容量,数组容量必须是2的n次方
allocateElements(c.size());
addAll(c);
}
/**
* Allocates empty array to hold the given number of elements.
* 分配数组来保存给定数量的元素。
* @param numElements the number of elements to hold
*/
private void allocateElements(int numElements) {
elements = new Object[calculateSize(numElements)];
}
// ****** Array allocation and resizing utilities ******
// 计算数组容量,必须是2 的n次幂
private static int calculateSize(int numElements) {
int initialCapacity = MIN_INITIAL_CAPACITY;
// Find the best power of two to hold elements.
// Tests "<=" because arrays aren't kept full.
if (numElements >= initialCapacity) {
initialCapacity = numElements;
initialCapacity |= (initialCapacity >>> 1);
initialCapacity |= (initialCapacity >>> 2);
initialCapacity |= (initialCapacity >>> 4);
initialCapacity |= (initialCapacity >>> 8);
initialCapacity |= (initialCapacity >>> 16);
initialCapacity++;
if (initialCapacity < 0) // Too many elements, must back off
initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements
}
return initialCapacity;
}
1.2.3 add方法
public boolean add(E e) {
addLast(e);
return true;
}
public void addLast(E e) {
if (e == null)
throw new NullPointerException();
elements[tail] = e;
// 下面条件满足:tail等于数组长度-1,说明当前数组中已经填充满了元素
if ( (tail = (tail + 1) & (elements.length - 1)) == head)
// 扩容
doubleCapacity();
}
public void addFirst(E e) {
if (e == null)
throw new NullPointerException();
// 第一次从前面添加的时候head = -1 & len-1 = len - 1;
elements[head = (head - 1) & (elements.length - 1)] = e;
if (head == tail)
doubleCapacity();
}
1.2.4 doubleCapacity方法
/**
* Doubles the capacity of this deque. Call only when full, i.e.,
* when head and tail have wrapped around to become equal.
* 双队列的容量加倍。只在满时调用,即: 当头部和尾部缠绕成相等的时候。
*/
private void doubleCapacity() {
assert head == tail;
int p = head;
int n = elements.length;
// p右边的元素
int r = n - p; // number of elements to the right of p
// 新的容量 = 旧容量 * 2
int newCapacity = n << 1;
if (newCapacity < 0)
throw new IllegalStateException("Sorry, deque too big");
Object[] a = new Object[newCapacity];
System.arraycopy(elements, p, a, 0, r);
System.arraycopy(elements, 0, a, r, p);
elements = a;
// 重新定值
head = 0;
tail = n;
}
2 List
2.1 ArrayList
2.1.1 类的静态字段和普通字段
// 序列号
private static final long serialVersionUID = 8683452581122892189L;
/**
* Default initial capacity.
* 默认初始容量大小
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for empty instances.
* 空数组,用于空实例,当实例化的时候传入0,如:new ArrayList<>(0); 那么elementData使用的就是这个
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 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.
* 用于默认大小空实例的共享空数组实例。
* 我们把它从EMPTY_ELEMENTDATA数组中区分出来,以知道在添加第一个元素时容量需要增加多少。
* 当实例化的时候没有指定长度,如:new ArrayList<>();
* 那么elementData使用的就是这个,添加第一个元素的时候容量扩展为DEFAULT_CAPACITY
*/
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.
* 保存ArrayList数据的数组
*/
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;
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
* 数组的最大容量
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
2.1.2 构造方法
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
* 定义初始容量的构造方法
*/
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.
* 无参构造方法
* DEFAULTCAPACITY_EMPTY_ELEMENTDATA 为0.初始化为10,也就是说初始其实是空数组 当添加第一个元素的时候数组容量才变成10
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
* 构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序。
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
// 如果elementData不是Object类型数据(c.toArray可能返回的不是Object类型的数组所以加上下面的语句用于判断)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
2.1.3 add方法
add方法 有两个不同参数的方法:
- 一个参数,首先判断容量是否足够,如果不够,那么扩容(grow()),然后在已有元素的最后添加元素。
- 两个参数(index, e) ,先对index进行界限检查(0<=index <= size),然后检查容量是否足够,不够就扩容,然后将从index索引开始的元素向后移动一位,最后将e添加到index位置。
有一点需要注意的是,如果在实例化的时候,没有指定初识容量,那么ArrayList会在add第一个值的时候,才会初始化容量,calculateCapacity() 这个方法就是用于判断这种情况。
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*
* 常见的增加元素的方法,直接添加到数组的末尾
*/
public boolean add(E e) {
// 用于判断容量是否够用,如果不够就会扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
/**
* 首先计算出当前的容量,然后去判断容量是否足够
* @param minCapacity
*/
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
/**
* 该方法是 返回最小的容量,当elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA时,说明是第一次添加元素,那么返回10,需要扩容,如果不是第一次,那么就返回最大的容量。
* @param elementData
* @param minCapacity
* @return
*/
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
/**
* 判断是否需要扩容
* @param minCapacity
*/
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
add(int index, E e);两个参数的方法
/**
* Inserts the specified element at the specified position in this
* list. Shifts the element currently at that position (if any) and
* any subsequent elements to the right (adds one to their indices).
*
* @param index index at which the specified element is to be inserted
* @param element element to be inserted
* @throws IndexOutOfBoundsException {@inheritDoc}
* 在此列表中的指定位置插入指定的元素。
* 先调用 rangeCheckForAdd 对index进行界限检查;然后调用 ensureCapacityInternal 方法保证capacity足够大;
* 再将从index开始之后的所有成员后移一个位置;将element插入index位置;最后size加1。
*/
public void add(int index, E element) {
// 检查索引是否合法
rangeCheckForAdd(index);
// 和一个参数的add方法是一样的
ensureCapacityInternal(size + 1); // Increments modCount!!
// 将index索引之后的元素向后移动一位
// System.arraycopy(源数组,起始位置,目标数组,起始位置,要复制的长度);
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
/**
* A version of rangeCheck used by add and addAll.
*/
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
// 我们发现 arraycopy 是一个 native 方法,接下来我们解释一下各个参数的具体意义
/**
* 复制数组
* @param src 源数组
* @param srcPos 源数组中的起始位置
* @param dest 目标数组
* @param destPos 目标数组中的起始位置
* @param length 要复制的数组元素的数量
*/
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
2.1.4 set方法
该方法:用于替换已有的元素。如果index< 0 那么 会抛出 :java.lang.ArrayIndexOutOfBoundsException: -1
首先验证index的合法性(index<size),然后获取旧的值,最后element替换旧的值,返回旧的值。
/**
* Replaces the element at the specified position in this list with
* the specified element.
*
* @param index index of the element to replace
* @param element element to be stored at the specified position
* @return the element previously at the specified position
* @throws IndexOutOfBoundsException {@inheritDoc}
* 替换指定索引上的值,索引必须小于size
*/
public E set(int index, E element) {
// 验证索引的合法性,需要注意的是这里的index需要小于size,也就是说set方法,只允许修改已添加元素的索引
rangeCheck(index);
// 获取旧的值
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
2.1.5 grow 方法
首先计算新数组容量,新数组容量 = 旧数组容量 + 旧数组容量 >> 1, 说明每次扩容增加 扩容前容量的一半**(oldCapacity 为偶数就是 1.5 倍,否则是 1.5 倍左右)!** 奇偶不同,比如 :10+10/2 = 15, 33+33/2=49。如果是奇数的话会丢掉小数.
然后检查这个计算出的容量是否足够,最后使用Arrays.copy() 拷贝一个新的数组。
扩容方法
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity 最小容量
* 扩容操作
*/
private void grow(int minCapacity) {
// overflow-conscious code
// 扩容之前的数组容量
int oldCapacity = elementData.length;
// 计算扩容之后的容量,右移1位就是 除以2, 说明每次扩容增加 扩容前容量的一半
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果增加一半的容量 小于 满足添加操作的最小容量,那么扩容容量 = 最小容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//再检查新容量是否超出了ArrayList所定义的最大容量,
//若超出了,则调用hugeCapacity()来比较minCapacity和 MAX_ARRAY_SIZE,
//如果minCapacity大于MAX_ARRAY_SIZE,则新容量则为Interger.MAX_VALUE,否则,新容量大小则为 MAX_ARRAY_SIZE。
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);
}
/**
* 计算 容量的最大值
* @param minCapacity
* @return
*/
private static int hugeCapacity(int minCapacity) {
// 这里就保证了最大容量 < Integer.MAX_VALUE,因为Integer.MAX_VALUE+ 1 < 0;
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
2.1.6 remove方法
remove 有两个方法
- remove(int i) 根据索引删除。检查索引,计算该索引之后右多少个元素,然后从该索引后一位向前拷贝一位,最后释放引用。
- remove(Object o) 根据数组中的元素删除,有多个相同的元素,只会删除索引小的那一个。先查询删除元素所在的索引,然后计算该索引之后右多少个元素,然后从该索引后一位向前拷贝一位,最后释放引用。
/**
* Removes the element at the specified position in this list.
* Shifts any subsequent elements to the left (subtracts one from their
* indices).
*
* @param index the index of the element to be removed
* @return the element that was removed from the list
* @throws IndexOutOfBoundsException {@inheritDoc}
* 这个remove是根据索引来删除元素
*/
public E remove(int index) {
rangeCheck(index); // 检查索引 index < size
modCount++; // 修改次数 + 1
// 获取要删除的元素
E oldValue = elementData(index); // 这里如果index < 0 会报数组越界异常
// 计算 要删除元素后面有多少个元素
int numMoved = size - index - 1;
if (numMoved > 0)
// 将 删除元素后面的元素拷贝到前一位
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 去掉最后一位的引用,便于JVM GC的时候就会回收
elementData[--size] = null; // clear to let GC do its work
// 返回删除元素的值
return oldValue;
}
/**
* Removes the first occurrence of the specified element from this list,
* if it is present. If the list does not contain the element, it is
* unchanged. More formally, removes the element with the lowest index
* <tt>i</tt> such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>
* (if such an element exists). Returns <tt>true</tt> if this list
* contained the specified element (or equivalently, if this list
* changed as a result of the call).
*
* @param o element to be removed from this list, if present
* @return <tt>true</tt> if this list contained the specified element
* 这个remove 是根据集合中的元素来删除
*/
public boolean remove(Object o) {
// 遍历集合找到要删除的值的索引,然后调用fastRemove(),只会删除遇到的第一个相同的元素
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 remove method that skips bounds checking and does not
* return the value removed.
*/
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; // clear to let GC do its work
}
2.1.7 System.arraycopy()和 Arrays.copyOf()方法
// 我们发现 arraycopy 是一个 native 方法,接下来我们解释一下各个参数的具体意义
/**
* 复制数组
* @param src 源数组
* @param srcPos 源数组中的起始位置
* @param dest 目标数组
* @param destPos 目标数组中的起始位置
* @param length 要复制的数组元素的数量
*/
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
public static int[] copyOf(int[] original, int newLength) {
// 申请一个新的数组
int[] copy = new int[newLength];
// 调用System.arraycopy,将源数组中的数据进行拷贝,并返回新的数组
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
arraycopy()
需要目标数组,将原数组拷贝到你自己定义的数组里或者原数组,而且可以选择拷贝的起点和长度以及放入新数组中的位置 copyOf()
是系统自动在内部新建一个数组,并返回该数组。
2.1.8 ensureCapacity方法
/**
如有必要,增加此 ArrayList 实例的容量,以确保它至少可以容纳由minimum capacity参数指定的元素数。
*
* @param minCapacity 所需的最小容量
*/
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
最好在 add 大量元素之前用 ensureCapacity 方法,以减少增量重新分配的次数
2.2 Vector
Vector 和 ArrayList 的一些方法的实现 基本相同,Vector对外提供访问的方法 除了构造方法都 使用了 synchronized 修饰。
通过下面的源码发现,每个方法中都添加了synchronized的关键字来保证同步,所以它是线程安全的,但正是这些方法的同步,让它的效率大大的降低了,比ArrayList的效率要慢。
2.2.1 类的静态字段和普通字段
/**
* The array buffer into which the components of the vector are
* stored. The capacity of the vector is the length of this array buffer,
* and is at least large enough to contain all the vector's elements.
*
* <p>Any array elements following the last element in the Vector are null.
*
* @serial
* 和ArrayList相同,使用Object[] 保存数据
*/
protected Object[] elementData;
/**
* The number of valid components in this {@code Vector} object.
* Components {@code elementData[0]} through
* {@code elementData[elementCount-1]} are the actual items.
*
* @serial
* 数组中 元素的数量 和ArrayList 的size相同。
*/
protected int elementCount;
/**
* The amount by which the capacity of the vector is automatically
* incremented when its size becomes greater than its capacity. If
* the capacity increment is less than or equal to zero, the capacity
* of the vector is doubled each time it needs to grow.
* 这个值如果大于0,那么在扩容的时候扩容之后的数组长度就等于扩容之前的数组长度 + 这个值
* 如果 小于等于0,那么在扩容的时候扩容之后的数组长度就等于扩容之前的数组长度 + 扩容之前的数组长度
* @serial
*/
protected int capacityIncrement;
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -2767605614048989439L;
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
* 要分配数组的最大大小
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
2.2.2 构造方法
Vector和ArrayList不同,Vector创建数组在构造方法中就进行了,如果没有指定那么数组长度就是10
/**
* Constructs an empty vector with the specified initial capacity and
* capacity increment.
*
* @param initialCapacity the initial capacity of the vector
* @param capacityIncrement the amount by which the capacity is
* increased when the vector overflows
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
public Vector(int initialCapacity, int capacityIncrement) {
super();
// 不能小于0
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
// 定义初始容量,然后创建一个数组
this.elementData = new Object[initialCapacity];
// capacityIncrement 每次扩容的时候增加的值
this.capacityIncrement = capacityIncrement;
}
/**
* Constructs an empty vector with the specified initial capacity and
* with its capacity increment equal to zero.
*
* @param initialCapacity the initial capacity of the vector
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
/**
* Constructs an empty vector so that its internal data array
* has size {@code 10} and its standard capacity increment is
* zero.
* 默认构造方法,那么初始容量就等于10
*/
public Vector() {
this(10);
}
/**
* Constructs a vector containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this
* vector
* @throws NullPointerException if the specified collection is null
* @since 1.2
*/
public Vector(Collection<? extends E> c) {
elementData = c.toArray();
elementCount = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}
2.2.3 add方法
synchronized修饰,锁当前对象
添加元素:
- add(E e); 添加元素到数组中元素的末尾,首先检查是否需要扩容,添加。
- add(int i, E e); 添加元素到指定索引位置,检查i的合法性 i <= elementCount,说明只能添加到已有元素的位置(包括末尾),检查是否需要扩容,拷贝,从i位置开始向后拷贝一位,添加到指定位置。
/**
* Appends the specified element to the end of this Vector.
*
* @param e element to be appended to this Vector
* @return {@code true} (as specified by {@link Collection#add})
* @since 1.2
*/
public synchronized boolean add(E e) {
modCount++; // 结构改变了就+1
// 判断是否需要扩容
ensureCapacityHelper(elementCount + 1);
// 添加到数组元素的末尾
elementData[elementCount++] = e;
return true;
}
/**
* This implements the unsynchronized semantics of ensureCapacity.
* Synchronized methods in this class can internally call this
* method for ensuring capacity without incurring the cost of an
* extra synchronization.
*
* @see #ensureCapacity(int)
* 判断是否需要扩容
*/
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
//minCapacity: 满足添加操作,最小的容量
if (minCapacity - elementData.length > 0)
//扩容
grow(minCapacity);
}
/**
* Inserts the specified element at the specified position in this Vector.
* Shifts the element currently at that position (if any) and any
* subsequent elements to the right (adds one to their indices).
*
* @param index index at which the specified element is to be inserted
* @param element element to be inserted
* @throws ArrayIndexOutOfBoundsException if the index is out of range
* ({@code index < 0 || index > size()})
* @since 1.2
* 向指定索引位置上添加元素
*/
public void add(int index, E element) {
insertElementAt(element, index);
}
/**
* Inserts the specified object as a component in this vector at the
* specified {@code index}. Each component in this vector with
* an index greater or equal to the specified {@code index} is
* shifted upward to have an index one greater than the value it had
* previously.
*
* <p>The index must be a value greater than or equal to {@code 0}
* and less than or equal to the current size of the vector. (If the
* index is equal to the current size of the vector, the new element
* is appended to the Vector.)
*
* <p>This method is identical in functionality to the
* {@link #add(int, Object) add(int, E)}
* method (which is part of the {@link List} interface). Note that the
* {@code add} method reverses the order of the parameters, to more closely
* match array usage.
*
* @param obj the component to insert
* @param index where to insert the new component
* @throws ArrayIndexOutOfBoundsException if the index is out of range
* ({@code index < 0 || index > size()})
*/
public synchronized void insertElementAt(E obj, int index) {
modCount++;// 结构改变 +1
// 判断index index <= elementCount
if (index > elementCount) {
throw new ArrayIndexOutOfBoundsException(index
+ " > " + elementCount);
}
// 判断是否需要扩容
ensureCapacityHelper(elementCount + 1);
// 和ArrayList使用的方法相同,从索引index开始向后拷贝一位
System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
// 添加元素
elementData[index] = obj;
elementCount++;
}
2.2.4 grow方法
和ArrayList的扩容不同之处在于:扩容后数组长度的计算。
ArrayList的扩容后数组长度等于旧数组长度 + 旧数组长度 >> 1
Vector的扩容数组长度等于旧数组长度+自定义的增加的值(如果该值<= 0), 那么就是旧数组长度+旧数组长度
/**
* 扩容方法
* @param minCapacity 要满足的最小容量
*/
private void grow(int minCapacity) {
// overflow-conscious code
// 扩容前的数组长度
int oldCapacity = elementData.length;
// 扩容之后的长度 = 扩容前数组长度 + (capacityIncrement 或 扩容前数组)
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
// 不能满足最小的容量,那么扩容后的数组长度 = 满足的最小容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 扩容之后的数组长度 大于 最大的容量
if (newCapacity - MAX_ARRAY_SIZE > 0)
// 新数组容量 = 如果要满足的最小容量大于数组最大容量,那么就等于Integer的最大值,否则等于数组最大容量
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
/**
* @param minCapacity 要满足的最小容量
* @return 如果要满足的最小容量大于数组最大容量,那么就等于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;
}
2.2.5 remove 方法
和ArrayList方法一样的实现,不同的是 使用了 synchronized 关键字修饰了。
/**
* Removes the element at the specified position in this Vector.
* Shifts any subsequent elements to the left (subtracts one from their
* indices). Returns the element that was removed from the Vector.
*
* @throws ArrayIndexOutOfBoundsException if the index is out of range
* ({@code index < 0 || index >= size()})
* @param index the index of the element to be removed
* @return element that was removed
* @since 1.2
*/
public synchronized E remove(int index) {
modCount++;
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
E oldValue = elementData(index);
int numMoved = elementCount - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--elementCount] = null; // Let gc do its work
return oldValue;
}
2.3 LinkedList
类的字段
// 版本序列号
private static final long serialVersionUID = 876323262645176354L;
/**
* 链表中节点的数量
*/
transient int size = 0;
/**
* Pointer to first node.
* Invariant不变量: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*
* 链表头节点
*/
transient Node<E> first;
/**
* Pointer to last node.
* Invariant 不变量: (first == null && last == null) ||
* (last.next == null && last.item != null)
* 链表尾节点
*/
transient Node<E> last;
Node节点
/**
* 链表中的节点
* @param <E>
*/
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;
}
}
构造方法
public LinkedList() {
}
/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
add方法
/**
* Appends the specified element to the end of this list.
*
* <p>This method is equivalent to {@link #addLast}.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
linkLast(e);
return true;
}
/**
* Appends the specified element to the end of this list.
*
* <p>This method is equivalent to {@link #add}.
*
* @param e the element to add
*/
public void addLast(E e) {
linkLast(e);
}
/**
* Links e as last element.
* 添加元素到链表末尾
*/
void linkLast(E e) {
//l:最后一个节点
final Node<E> l = last;
//创建一个新的节点
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
// l == null 说明 链表中还没有节点,添加第一个节点
if (l == null)
first = newNode;
else
l.next = newNode;
// 节点数量+1
size++;
modCount++;
}
/**
* Inserts the specified element at the beginning of this list.
*
* @param e the element to add
*/
public void addFirst(E e) {
linkFirst(e);
}
/**
* 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++;
}
/**
* Inserts all of the elements in the specified collection into this
* list, starting at the specified position. Shifts the element
* currently at that position (if any) and any subsequent elements to
* the right (increases their indices). The new elements will appear
* in the list in the order that they are returned by the
* specified collection's iterator.
*
* @param index index at which to insert the first element
* from the specified collection
* @param c collection containing elements to be added to this list
* @return {@code true} if this list changed as a result of the call
* @throws IndexOutOfBoundsException {@inheritDoc}
* @throws NullPointerException if the specified collection is null
*/
public boolean addAll(int index, Collection<? extends E> c) {
//检查index, 0<=index<=size
checkPositionIndex(index);
Object[] a = c.toArray();
int numNew = a.length;
if (numNew == 0)
return false;
// pred:要插入节点的前一个节点
// succ: index索引位置的节点
Node<E> pred, succ;
// index == size 说明 向链表尾部添加节点
if (index == size) {
succ = null;
pred = last;
} else { //说明index < size 向指定索引位置添加节点
succ = node(index);
pred = succ.prev;
}
// 遍历数组
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
Node<E> newNode = new Node<>(pred, e, null);
// 说明第一次添加
if (pred == null)
first = newNode;
else
pred.next = newNode;
// 新添加的节点变成前一个节点
pred = newNode;
}
// 说明 数组是向链表尾部添加的节点,last = 最后添加的节点即可
if (succ == null) {
last = pred;
} else {// 说明 数组向链表指定位置添加的节点,那么需要将index位置以及后面的节点重新放入链表中
pred.next = succ;
succ.prev = pred;
}
// 节点增加
size += numNew;
modCount++;
return true;
}
get方法
/**
* Returns the element at the specified position in this list.
*
* @param index index of the element to return
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
// 检查索引 0 <= index < size
checkElementIndex(index);
return node(index).item;
}
/**
* Returns the (non-null) Node at the specified element index.
*
* 返回索引处的节点
*/
Node<E> node(int index) {
// assert isElementIndex(index);
// index 小于 size / 2 说明 索引处节点在链表的左半部分,那么从头遍历
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;
}
}
remove方法
/**
* Removes the first occurrence of the specified element from this list,
* if it is present. If this list does not contain the element, it is
* unchanged. More formally, removes the element with the lowest index
* {@code i} such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>
* (if such an element exists). Returns {@code true} if this list
* contained the specified element (or equivalently, if this list
* changed as a result of the call).
*
* @param o element to be removed from this list, if present
* @return {@code true} if this list contained the specified element
*/
public boolean remove(Object o) {
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
/**
* Removes the element at the specified position in this list. Shifts any
* subsequent elements to the left (subtracts one from their indices).
* Returns the element that was removed from the list.
*
* @param index the index of the element to be removed
* @return the element previously at the specified position
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E remove(int index) {
// 检查索引 0 <= index < size
checkElementIndex(index);
return unlink(node(index));
}
/**
* Unlinks non-null node x.
* 删除x节点
*/
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null; // 取消引用,方便JVM GC
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null; // 取消引用,方便JVM GC
}
x.item = null;
size--;
modCount++;
return element;
}
3 Set
3.1 HashSet源码
HashSet就是对HashMap的一些封装。
类的常量和字段
从字段中就可以看出hashSet 就是基于HashMap扩展的,内部使用的就是map,value值使用一个空对象代替
//版本序列号
static final long serialVersionUID = -5024744406713321676L;
// HashSet 就是基于map 扩展的 使用map的key,value用一个空对象代替
private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map
// 作为Map的value值
private static final Object PRESENT = new Object();
构造方法
有5个构造方法,都是创建一个HashMap,也说明了hashSet 就是 使用的hashMap。
/**
* Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
* default initial capacity (16) and load factor (0.75).
*/
public HashSet() {
map = new HashMap<>();
}
/**
* Constructs a new set containing the elements in the specified
* collection. The <tt>HashMap</tt> is created with default load factor
* (0.75) and an initial capacity sufficient to contain the elements in
* the specified collection.
*
* @param c the collection whose elements are to be placed into this set
* @throws NullPointerException if the specified collection is null
*/
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
/**
* Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
* the specified initial capacity and the specified load factor.
*
* @param initialCapacity the initial capacity of the hash map
* @param loadFactor the load factor of the hash map
* @throws IllegalArgumentException if the initial capacity is less
* than zero, or if the load factor is nonpositive
*/
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
/**
* Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
* the specified initial capacity and default load factor (0.75).
*
* @param initialCapacity the initial capacity of the hash table
* @throws IllegalArgumentException if the initial capacity is less
* than zero
*/
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
/**
* Constructs a new, empty linked hash set. (This package private
* constructor is only used by LinkedHashSet.) The backing
* HashMap instance is a LinkedHashMap with the specified initial
* capacity and the specified load factor.
*
* @param initialCapacity the initial capacity of the hash map
* @param loadFactor the load factor of the hash map
* @param dummy ignored (distinguishes this
* constructor from other int, float constructor.)
* @throws IllegalArgumentException if the initial capacity is less
* than zero, or if the load factor is nonpositive
*/
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
看一下HashSet内部的方法
一些 size()
,isEmpty()
,contains()
,add()
,remove()
等方法都是直接调用的hashMap的方法。没有什么可看的。
3.2 LinkedHashSet源码
public class LinkedHashSet<E>
extends HashSet<E>
implements Set<E>, Cloneable, java.io.Serializable {
可以看到LinkedHashSet 继承了HashSet,那么就可以使用HashSet的一些公有方法。
LinkedHashSet中只有4个构造方法,没有其它留个外部的方法.
public class LinkedHashSet<E>
extends HashSet<E>
implements Set<E>, Cloneable, java.io.Serializable {
private static final long serialVersionUID = -2851667679971038690L;
/**
* Constructs a new, empty linked hash set with the specified initial
* capacity and load factor.
*
* @param initialCapacity the initial capacity of the linked hash set
* @param loadFactor the load factor of the linked hash set
* @throws IllegalArgumentException if the initial capacity is less
* than zero, or if the load factor is nonpositive
*/
public LinkedHashSet(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor, true);
}
/**
* Constructs a new, empty linked hash set with the specified initial
* capacity and the default load factor (0.75).
*
* @param initialCapacity the initial capacity of the LinkedHashSet
* @throws IllegalArgumentException if the initial capacity is less
* than zero
*/
public LinkedHashSet(int initialCapacity) {
super(initialCapacity, .75f, true);
}
/**
* Constructs a new, empty linked hash set with the default initial
* capacity (16) and load factor (0.75).
*/
public LinkedHashSet() {
super(16, .75f, true);
}
/**
* Constructs a new linked hash set with the same elements as the
* specified collection. The linked hash set is created with an initial
* capacity sufficient to hold the elements in the specified collection
* and the default load factor (0.75).
*
* @param c the collection whose elements are to be placed into
* this set
* @throws NullPointerException if the specified collection is null
*/
public LinkedHashSet(Collection<? extends E> c) {
super(Math.max(2*c.size(), 11), .75f, true);
addAll(c);
}
/**
* Creates a <em><a href="Spliterator.html#binding">late-binding</a></em>
* and <em>fail-fast</em> {@code Spliterator} over the elements in this set.
*
* <p>The {@code Spliterator} reports {@link Spliterator#SIZED},
* {@link Spliterator#DISTINCT}, and {@code ORDERED}. Implementations
* should document the reporting of additional characteristic values.
*
* @implNote
* The implementation creates a
* <em><a href="Spliterator.html#binding">late-binding</a></em> spliterator
* from the set's {@code Iterator}. The spliterator inherits the
* <em>fail-fast</em> properties of the set's iterator.
* The created {@code Spliterator} additionally reports
* {@link Spliterator#SUBSIZED}.
*
* @return a {@code Spliterator} over the elements in this set
* @since 1.8
*/
@Override
public Spliterator<E> spliterator() {
return Spliterators.spliterator(this, Spliterator.DISTINCT | Spliterator.ORDERED);
}
}
注意:构造方法中调用的父类的构造方法都是同一个(下面的),创建的是一个LInkedHashMap,所以可以实现有序性。
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}