Java集合类(下)

前面我们按照接口为线索,分析了和集合类相关的一些接口以及他们的实现类,下面我们将针对具体的类来分析其具体实现。

我们将了解到容器的存储实现、构造方法、常用方法。

一、ArrayList

public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
  1. 查看ArrayList类的定义可知,他继承了AbstractList(有了list接口实现类的方法)
  2. 实现了List接口
  3. 实现了RandomAccess, Cloneable, java.io.Serializable,说明它是可随时访问的,可克隆的,可被序列化的实现类

1.1 属性

private static final long serialVersionUID = 8683452581122892189L;

// 默认初始化大小
private static final int DEFAULT_CAPACITY = 10;

// 空数组,供空实例使用
private static final Object[] EMPTY_ELEMENTDATA = {};

// 与EMPTY_ELEMENTDATA区分开,以了解添加第一个元素时的膨胀程度
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

// 存储元素的数组。ArrayList容量是这个数组的长度。
// elementData==DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空ArrayList在添加第一个元素时会扩充为DEFAULT_CAPACITY = 10
// 添加元素时,就会被扩充为10个长度的数组大小
transient Object[] elementData; // non-private to simplify nested class access

// ArrayList的size,包含的元素数量
private int size;

elementData被transient关键字修饰了,这个关键字的意思是不会被序列化,但这与Serializable接口似乎自相矛盾。

原因:由于ArrayList是动态扩容的,所以数组中可能存在没有存储元素的单元,如果采用外部的序列化实现的话,就会序列化整个数组,没有存储的空间就会被序列化,浪费时间和空间。

解决:重写序列化及反序列化方法,我们后面会看到 ArrayList 有 writeObject 和 readObject 两个方法,实际上就是序列化和反序列化的方法。

1.2 构造方法

ArrayList有三个构造方法

  1. 第一个传入初始大小,当 ArrayList 新增元素时,如果空间不足,会进行动态扩容,会导致整个数组进行一次内存复制。因此,在初始化 ArrayList 时,可以预估数组大小,这样可以减少扩容次数,提高系统性能
  2. 第二个是空参构造,数组默认引用DEFAULTCAPACITY_EMPTY_ELEMENTDATA。
  3. 第三个传入集合,通过toArray方法转化为数组,引用赋给数组,如果集合长度为0,数组引用EMPTY_ELEMENTDATA。
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
// 初始化数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
// 初始化空数组,规定初始化大小就是0
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}

public ArrayList() {
// 默认空数组,初始化大小不知道,和EMPTY_ELEMENTDATA区分
// 在后面还会提到,被赋值为此DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空ArrayList在添加第一个元素时会扩充为DEFAULT_CAPACITY = 10
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

//传入集合
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;
}
}

1.3 方法

(1)add方法

public void add(int index, E element) {
rangeCheckForAdd(index);
// 确保空间足够
ensureCapacityInternal(size + 1); // Increments modCount!!
// 将index后面元素后移,空出index索引位置
// System.arraycopy()方法,是将数组中从index起后面的元素赋值到数组中从index+1起后面,大小为size-index
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}

public boolean addAll(Collection<? extends E> c) {
// 转换成数组
Object[] a = c.toArray();
int numNew = a.length;
// 确保空间足够
ensureCapacityInternal(size + numNew); // Increments modCount
// 追加到末尾
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}

public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
// 转换成数组
Object[] a = c.toArray();
int numNew = a.length;
// 确保空间足够
ensureCapacityInternal(size + numNew); // Increments modCount
// 要移动的元素数量
int numMoved = size - index;
if (numMoved > 0)
// index后面元素向后移动numNew距离
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
// 添加元素
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}

(2)remove方法

public E remove(int index) {
// 保证index合法
rangeCheck(index);

modCount++;
// 返回旧值
E oldValue = elementData(index);

int numMoved = size - index - 1;
if (numMoved > 0)
// 将index后面元素前移一个单元
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 最后一个空间置空
elementData[--size] = null; // clear to let GC do its work

return oldValue;
}

// 遍历,调用fastRemove方法
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;
}

// 相比remove空参方法,省去了index检查,返回值,
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
}

// 删除c中存在的元素
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
// 删除c中不存在的
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, true);
}

// 上面两个在ArrayList中删除集合元素的方法,都通过该方法实现
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0; // r为遍历索引 w为结果索引
boolean modified = false;
try {
for (; r < size; r++)
// complement为false,保存c中不存在的元素
// complement为true,保存c中存在的元素
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
// 存在并发修改,
if (r != size) {
// 如果存在并发删除,则size - r为负数
// System.arraycopy文档中有说明,会抛出IndexOutOfBoundsException运行时异常
// 如果存在并发添加,则将添加的元素追加到w索引后面。
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
// 成功删除了元素,将后面空间置空
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}


public void clear() {
modCount++;

// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;

size = 0;
}

(3)indexOf方法

public boolean contains(Object o) {
return indexOf(o) >= 0;
}

public E get(int index) {
rangeCheck(index);

return elementData(index);
}

// 这种处理方法我们见过很多次了,针对o是否为空,分别处理
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
// 参考indexOf,区别在于遍历是倒序的,适用于知道o是在后面的位置
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}

(4)ensureCapacityInternal关键方法

在添加方法中都调用了ensureCapacityInternal来保证数组空间大小,下面我们分析一下。

private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

// 首先判断是否空参构造,elementData数组 == DEFAULTCAPACITY_EMPTY_ELEMENTDATA?
// 如果是的话,返回Math.max(默认数组大小10, 参数minCapacity)
// 否则返回参数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在这里+1,难怪在添加方法中找不到
modCount++;

// overflow-conscious code
// 如果空间不够了,就要扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}

// 真正的扩容函数
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 扩容 1.5 倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 还不够的话,扩容到参数minCapacity
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果超过了最大数组大小,根据参数minCapacity的大小需要,进行设置
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
// elementData引用到新数组,旧数组等待GC,所以我们要尽量避免扩容操作
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;
}

(5)其他方法

// 缩减数组大小到元素数量
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}

// 重写了AbstractCollection的方法,效率更高
public Object[] toArray() {
// 返回副本
return Arrays.copyOf(elementData, size);
}

public <T> T[] toArray(T[] a) {
// a的空间不够,新拷贝一个数组返回
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
// a的空间足够,将elementData复制给a,返回a
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}

二、Vector

Vector的实现和ArrayList基本相同,二者的区别是,Vector是线程安全的,其写方法都是syncronized修饰的,所以我们不再赘述。

三、Stack

Stack是栈的实现,继承了Vector,所以也是线程安全的。

但是Deque接口及其实现提供了一组更完整、更一致的后进先出堆栈操作,应该优先使用Deque来实现栈

Stack的JavaDoc中有这样一段话:

在这里插入图片描述

// 翻译参考:
// Deque接口及其实现提供了一组更完整、更一致的后进先出堆栈操作,应该优先使用这些操作。例如:
// Deque<Integer> stack = new ArrayDeque<Integer>();

当然,我们在Deque的文档中也找到了相关说明:

在这里插入图片描述

// 翻译参考:
// Deques也可以作为后进先出的堆叠。这个接口应该优先用于遗留堆栈类。
// 当deque用作堆栈时,元素从deque的开头被推入和弹出。
// 如表所示,堆栈方法与Deque方法完全等价:

所以,当没有并发要求时,我们应该优先使用Deque实现栈。所以我们简单看一下Stack类的源码。

// 短短不到50行,非常简单,对列表操作进行限制,就是栈了。
// 需要注意的是 三个同步方法的synchronized是为了保证size()获取的共享变量的可见性
public class Stack<E> extends Vector<E> {

public Stack() {
}

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;
}

四、Queue接口

Queue接口主要定义了队列的基本方法,下面我们分析注释,了解其实现、使用规则,是Collection的子接口。

/**
* 设计用于在处理之前保留元素的集合。 除了基本的Collection操作之外,队列还提供额外的插入,提取和检查操作。
* 这些方法中的每一种都有两种形式:如果操作失败,则抛出一个异常,
* 另一种形式返回一个特殊值( null或false ,具体取决于操作)。
* 插入操作的后一种形式专门设计用于容量限制的Queue实现; 在大多数实现中,插入操作不能失败。
*
* 队列通常(但不一定)是以FIFO(先进先出)方式排序元素。
* 例外情况包括优先级队列(根据提供的比较器对元素进行排序)和LIFO队列(或堆栈)(后进先出)。
* 无论使用什么顺序,队列的头都是通过调用remove()或poll()删除的元素。
* 在一个FIFO队列,所有新元素插入到队列的尾部。其他类型的队列可能使用不同的布局规则。
* 每个Queue实现必须指定其排序属性。
*
* offer方法插入一个元素,如果失败返回false。这与Collection.add方法失败只能通过抛出异常不同。
* offer方法设计在故障情况下是正常的,而不是发生异常,例如在固定容量(或“有界”)队列中。
*
* remove()和poll()方法删除并返回队列的头。从队列中删除哪个元素是队列排序策略的一个功能,它与实现不同。
* remove()和poll()方法在队列为空时的行为不同:remove()方法抛出异常,而poll()方法返回null 。
*
* element()和peek()方法返回队列的头元素,但不删除。
*
* Queue接口没有定义在并发编程中是常见的阻塞队列方法。
* 这些方法(等待元素添加或有空间可用)在BlockingQueue接口中定义,该接口扩展了此接口。
*
* Queue实现通常不允许插入null元素,尽管一些实现(例如LinkedList)允许插入null。
* 即使在允许它的实现中,null也不应该插入到Queue中,
* 因为null也被poll方法用作特殊的返回值,表明队列不包含元素。
*
* Queue实现通常不定义基于元素的equals方法和hashCode方法,而是从类别Object继承基于标识的版本,
* 因为对于具有相同元素但不同排序属性的队列,基于元素的等式并不总是定义良好。
*/
public interface Queue<E> extends Collection<E> {
/**
* 插入指定元素到队列中,如果不会违反容量限制,在成功后返回true
* 如果没有空间可用,抛出IllegalStateException异常
* @param e the element to add
* @return {@code true} (as specified by {@link Collection#add})
* @throws IllegalStateException 由于容量限制,元素不能被添加
* @throws ClassCastException 如果指定元素的类阻止将其添加到此队列中
* @throws NullPointerException 如果指定的元素为空,并且此队列不允许添加空元素
* @throws IllegalArgumentException 如果此元素的某些属性阻止将其添加到此队列中
*/
boolean add(E e);

/**
* 插入指定元素到队列中,如果不会违反容量限制
* 当使用容量受限的队列时,此方法通常比add方法更可取,后者插入元素失败抛出异常。
* @param e the element to add
* @return 如果元素被添加到此队列,则为true,否则为false
* @throws ClassCastException 如果指定元素的类阻止将其添加到此队列中
* @throws NullPointerException 如果指定的元素为空,并且此队列不允许添加空元素
* @throws IllegalArgumentException 如果此元素的某些属性阻止将其添加到此队列中
*/
boolean offer(E e);

/**
* 返回并删除队列的头元素。这个方法和poll方法不同的地方仅在于如果队列为空的话,remove方法会抛出异常。
* @throws NoSuchElementException 如果队列为空
*/
E remove();

/**
* 返回并删除队列的头元素,如果队列为空,返回null。
*/
E poll();

/**
* 返回队列头元素,但是不删除他。这个方法和peek方法的唯一区别是,当队列为空时,element方法会抛出异常。
* @throws NoSuchElementException 如果队列为空
*/
E element();

/**
* 返回队列的头元素,但不删除他,如果队列为空的话,返回null。
*/
E peek();
}

4.1 AbstractQueue抽象类

/**
* 该类提供一些队列操作的框架实现。
* 当基本实现不允许空元素时,该类中的实现是适当的。
* add方法、remove方法和element方法分别基于offer方法、poll方法、peek方法
* 但是通过抛出异常而不是返回false或null来表明失败
*
* 扩展这个类的队列实现必须最少定义一个不允许插入null元素的offer方法,以及peek、poll、size、itertor方法。
* 通常还会重写其他方法。如果不能满足这些需求,可以考虑子类化AbstractCollection
*/
public abstract class AbstractQueue<E>
extends AbstractCollection<E>
implements Queue<E> {

/**
* 给子类使用的构造器
*/
protected AbstractQueue() {
}

/**
* 如果offer成功,返回true,否则抛出IllegalStateException异常
*/
public boolean add(E e) {
// 基于offer方法
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}

/**
* 返回poll的结果,除非队列为空
*/
public E remove() {
// 基于poll方法
E x = poll();
if (x != null)
return x;
else
throw new NoSuchElementException();
}

/**
* 返回peek的结果,除非队列为空
*/
public E element() {
// 基于peek方法
E x = peek();
if (x != null)
return x;
else
throw new NoSuchElementException();
}

/**
* 删除队列中所有元素,重复调用poll方法,直到其返回null
*/
public void clear() {
while (poll() != null)
;
}

/**
* 将指定集合中的所有元素添加到此队列。
* 试图将所有队列添加到自身会导致IllegalArgumentException。
* 此外,如果在操作进行过程中修改了指定的集合,则此操作的行为是未定义的。
*
* 此实现遍历指定的集合,并依次将迭代器返回的每个元素添加到此队列。
* 在尝试添加元素(特别是包含null元素)时遇到的运行时异常可能会导致在抛出异常时成功添加了一部分元素。
*
* @param c 包含要添加到队列中的元素
* @return 如果此队列因调用而更改,返回true
* @throws ClassCastException 如果指定集合的元素的类阻止将其添加到此队列中
* @throws NullPointerException 如果指定的集合包含空元素,且此队列不允许空元素,或者指定的集合为空
* @throws IllegalArgumentException 如果指定集合的元素的某些属性阻止将其添加到此队列,或者指定的集合是此队列
* @throws IllegalStateException 如果由于插入限制,此时不能添加所有元素
*/
public boolean addAll(Collection<? extends E> c) {
if (c == null)
throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
boolean modified = false;
for (E e : c)
// 基于add方法
if (add(e))
modified = true;
return modified;
}
}

4.2 Deque接口(LinkedList实现它)

deque:双端队列 ,支持在两端插入和移除元素的线性集合,deque是“double ended queue”的缩写,通常读作“deck”。

/**
* 大多数Deque实现对包含的元素的数量没有固定的限制,
* 这个接口支持容量受限的deque以及没有固定大小限制的deque。
*
* 这个接口定义了访问deque两端元素的方法。
* 提供了用于插入、删除和检查元素的方法。
* 这些方法都以两种形式存在:一种方法在操作失败时抛出异常,另一种方法返回特殊值(null或false,取决于操作)。
* 后一种插入操作是专门为容量受限的Deque实现而设计的;在大多数实现中,插入操作不会失败。
*
* 以上12种方法总结如下表:
**/

在这里插入图片描述

/**
* 这个接口扩展了Queue接口。当deque用作队列时,FIFO(先进先出)行为将产生。
* 元素添加在deque的末尾,并从开头删除。从Queue接口继承的方法与Deque方法完全等价,如下表所示:
**/
/**
* Deques也可以作为后进先出的堆栈。这个接口应该优先用于遗留Stack类。
* 当deque用作堆栈时,元素从deque的开头被推入和弹出。(只用个开头)
* 如下表所示,堆栈方法与Deque方法完全等价:
**/

在这里插入图片描述

/**
* 注意,当deque被用作队列或堆栈时,peek方法也同样有效;在这两种情况下,元素都是从deque的开头抽取的。
*
* 这个接口提供了两种方法来删除内部元素,removeFirstOccurrence和removeLastOccurrence。
*
* 与List接口不同,此接口不支持对元素的索引访问。
*
* 虽然并不严格要求Deque实现禁止插入空元素,但强烈建议这样做。
* 强烈建议使用任何允许空元素的Deque实现的用户不要使用其插入空值的能力。
* 这是因为null被各种方法用作特殊的返回值,以表明deque是空的。
*
* Deque实现通常不定义equals和hashCode方法的基于元素的版本,而是从类对象继承基于身份的版本。
*
* 该接口是Java集合框架的成员。
*/
public interface Deque<E> extends Queue<E> {
/**
* 如果不违反容量限制,则在deque的头部插入指定的元素。
* 如果当前没有空间可用,抛出IllegalStateException异常。
* 当使用容量受限的deque时,通常最好使用方法offerFirst。
* @param e 要插入的元素
* @throws IllegalStateException 如果由于容量限制,此时不能添加元素
* @throws ClassCastException 如果指定元素的类阻止它被添加到此deque
* @throws NullPointerException 如果指定的元素为空,并且deque不允许空元素
* @throws IllegalArgumentException 如果指定元素的某些属性阻止将其添加到此deque中
*/
void addFirst(E e);

/**
* 如果不违反容量限制,则在deque的尾部插入指定的元素。
* 如果当前没有空间可用,抛出IllegalStateException异常。
* 当使用容量受限的deque时,通常最好使用方法offerLast。
*
* 这个方法等同于add方法。
*
* @param e 要插入的元素
* @throws IllegalStateException 如果由于容量限制,此时不能添加元素
* @throws ClassCastException 如果指定元素的类阻止它被添加到此deque
* @throws NullPointerException 如果指定的元素为空,并且deque不允许空元素
* @throws IllegalArgumentException 如果指定元素的某些属性阻止将其添加到此deque中
*/
void addLast(E e);

/**
* 如果不违反容量限制,则在deque的头部插入指定的元素。
* 当使用容量受限的deque时,此方法通常比addFirst方法更好,后者可能无法插入元素,而只是抛出一个异常。
*
* @param e 要插入的元素
* @throws IllegalStateException 如果由于容量限制,此时不能添加元素
* @throws ClassCastException 如果指定元素的类阻止它被添加到此deque
* @throws NullPointerException 如果指定的元素为空,并且deque不允许空元素
* @throws IllegalArgumentException 如果指定元素的某些属性阻止将其添加到此deque中
*/
boolean offerFirst(E e);

/**
* 如果不违反容量限制,则在deque的尾部插入指定的元素。
* 当使用容量受限的deque时,此方法通常比addLast方法更好,后者可能无法插入元素,而只是抛出一个异常。
*
* @param e 要插入的元素
* @throws IllegalStateException 如果由于容量限制,此时不能添加元素
* @throws ClassCastException 如果指定元素的类阻止它被添加到此deque
* @throws NullPointerException 如果指定的元素为空,并且deque不允许空元素
* @throws IllegalArgumentException 如果指定元素的某些属性阻止将其添加到此deque中
*/
boolean offerLast(E e);

/**
* 返回并且移出队列的第一个元素。这个方法不同于pollFirst只在于,当队列为空,本方法抛出异常。
* @throws NoSuchElementException 如果队列为空
*/
E removeFirst();

/**
* 返回并且移出队列的最后一个元素。这个方法不同于pollLast只在于,当队列为空,本方法抛出异常。
* @throws NoSuchElementException 如果队列为空
*/
E removeLast();

/**
* 返回并且移出队列的第一个元素,当队列为空时,返回null
*/
E pollFirst();

/**
* 返回并且移出队列的最后一个元素,当队列为空时,返回null
*/
E pollLast();

/**
* 返回,但不移出队列的第一个元素。这个方法不同于peekFirst方法只在于,当队列为空,本方法抛出异常。
* @throws NoSuchElementException 如果队列为空
*/
E getFirst();

/**
* 返回,但不移出队列的最后一个元素。这个方法不同于peekLast方法只在于,当队列为空,本方法抛出异常。
* @throws NoSuchElementException 如果队列为空
*/
E getLast();

/**
* 返回,但不移出队列的第一个元素。当队列为空,返回null。
*/
E peekFirst();

/**
* 返回,但不移出队列的最后一个元素。当队列为空,返回null。
*/
E peekLast();

/**
* 从deque中移除指定元素的第一个匹配项。如果deque不包含该元素,它将保持不变。
* 更正式地说,删除第一个元素o(如果存在这样的元素)。
* 如果这个deque包含指定的元素,则返回true(或者等价地,如果这个deque因为调用而改变)。
*
* @throws ClassCastException 如果指定元素的类与此deque不兼容
* @throws NullPointerException 如果指定的元素为空,并且deque不允许空元素
*/
boolean removeFirstOccurrence(Object o);

/**
* 从deque中移除指定元素的最后一个匹配项。如果deque不包含该元素,它将保持不变。
* 更正式地说,删除最后一个元素o(如果存在这样的元素)。
* 如果这个deque包含指定的元素,则返回true(或者等价地,如果这个deque因为调用而改变)。
* @throws ClassCastException 如果指定元素的类与此deque不兼容
* @throws NullPointerException 如果指定的元素为空,并且deque不允许空元素
*/
boolean removeLastOccurrence(Object o);

// *** Queue methods ***

/**
* 这个方法等同于addLast。
*/
boolean add(E e);

/**
* 此方法等同于offerLast
*/
boolean offer(E e);

/**
* 此方法等同于removeFirst
*/
E remove();

/**
* 此方法等同于pollFirst
*/
E poll();

/**
* 此方法等同于getFirst
*/
E element();

/**
* 此方法等同于peekFirst
*/
E peek();


// *** Stack methods ***

/**
* 此方法等同于addFirst
*/
void push(E e);

/**
* 此方法等同于removeFirst
*/
E pop();


// *** Collection methods ***

/**
* 从deque中移除指定元素的第一个匹配项。如果deque不包含该元素,则它将保持不变。
* 更正式地说,删除第一个元素(如果存在这样一个元素)。
* 如果这个deque包含指定的元素,则返回true(或者同样地,如果这个deque由于调用而改变)。
*
* 这个方法相当于removeFirstOccurrence(Object)方法。
*
* @throws ClassCastException 如果指定元素的类与此deque不兼容
* @throws NullPointerException 如果指定的元素为空,并且deque不允许空元素
*/
boolean remove(Object o);

/**
* 如果该deque包含指定的元素,则返回true。
* 更正式地说,当且仅当deque至少包含一个元素o时,返回true
*
* @param o 将被测试,是否存在在这个deque中的元素
* @return 如果这个deque包含指定的元素,则为true
* @throws ClassCastException 如果指定元素的类型与此deque不兼容
* @throws NullPointerException 如果指定的元素为空,并且deque不允许空元素
*/
boolean contains(Object o);

/**
* 返回deque中的元素数量。
*/
public int size();

/**
* 按适当的顺序返回deque中元素的迭代器。元素将按从第一个(head)到最后一个(tail)的顺序返回。
*/
Iterator<E> iterator();

/**
* 以相反的顺序返回deque中元素的迭代器。元素将按从最后(尾)到第一个(头)的顺序返回。
*/
Iterator<E> descendingIterator();

}

五、AbstractSequentialList(LinkedList为其子类)

AbstractSequentialList:顺序列表

  • 相对RandomAccess标志接口而言的,表示容器支持“随机访问”(假随机,是通过迭代遍历),这类容器会优先使用索引进行操作。

  • AbstractSequentialList不支持随机访问的容器,通常使用迭代器进行操作

    中实现了List中的一些基本操作,都是基于迭代器的实现,下面我们看一下源码。

/**
* 该类提供List接口的框架实现,以最小化由“顺序访问”数据存储(如链表)支持的实现该接口所需的工作。
* 对于随机访问数据(例如数组),应该优先使用AbstractList。
*
* 这个类与AbstractList类相反,因为它另外实现了list iterator的“随机访问”方法
* get(int index)、set(int index、E element)、add(int index、E element)和remove(int index)
* 而不是其他方式。
*
* 要实现一个列表,程序员只需继承这个类并为listIterator和size方法提供实现。
* 对于不可修改的列表,程序员只需实现列表迭代器的hasNext、next、hasPrevious、previous和index方法。

* 对于可修改的列表,程序员还应该实现列表迭代器的set方法。
* 对于可变大小的列表,程序员还应该实现列表迭代器的删除和添加方法。
*
* 按照集合接口规范中的建议,程序员通常应该提供一个void(无参数)和集合构造函数。
* 该类是Java集合框架的成员。
*/
public abstract class AbstractSequentialList<E> extends AbstractList<E> {

protected AbstractSequentialList() {
}

public E get(int index) {
try {
// 通过list迭代器实现返回index位置元素 listIterator(index).next()
return listIterator(index).next();
} catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index);
}
}


// 如果list没有实现set方法,则抛出UnsupportedOperationException
public E set(int index, E element) {
try {
// 通过list迭代器实现
ListIterator<E> e = listIterator(index);
E oldVal = e.next();
e.set(element);
return oldVal;
} catch (NoSuchElementException exc) {
// 方便程序员定位异常
throw new IndexOutOfBoundsException("Index: "+index);
}
}


// 如果list没有实现add方法,则抛出UnsupportedOperationException
public void add(int index, E element) {
try {
// 通过list迭代器实现
listIterator(index).add(element);
} catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index);
}
}


// 如果list没有实现remove方法,则抛出UnsupportedOperationException
public E remove(int index) {
try {
// 通过list迭代器实现
ListIterator<E> e = listIterator(index);
E outCast = e.next();
e.remove();
return outCast;
} catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index);
}
}


// Bulk Operations

// 如果listIterator没有实现add方法,则抛出UnsupportedOperationException
public boolean addAll(int index, Collection<? extends E> c) {
try {
boolean modified = false;
ListIterator<E> e1 = listIterator(index);
Iterator<? extends E> e2 = c.iterator();
while (e2.hasNext()) {
e1.add(e2.next());
modified = true;
}
return modified;
} catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index);
}
}

// 返回list迭代器
public Iterator<E> iterator() {
return listIterator();
}


public abstract ListIterator<E> listIterator(int index);
}

六、LinkedList

6.1 定义

public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
  1. 可以看到LinkedList继承了AbstractSequentialList抽象类,说明LinkedList是一种顺序列表(不支持随机存储,迭代器实现)。

  2. LinkedList实现了Cloneable和Serializable接口,我们之前分析过了,他是可克隆可序列化的。

  3. 实现了List接口、Deque双端队列接口(所以LinkedList是双向链表)、以及底层是迭代器。

6.2 Node

LinkedList是使用Node来存储数据的,我们在学习数据结构的时候已经对链表的结构有所了解。可以看出,LinkedList是双向链表,Node中除了item元素,还有next下一个节点引用,prev上一个节点引用。

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;
}
}

6.3 属性

LinkedList有三个属性size、first、last,分别保存链表大小、第一个节点的引用、最后一个节点引用。

transient int size = 0;

transient Node<E> first;

transient Node<E> last;

同样使用transient关键字,避免空节点被序列化,避免性能损耗。

6.4 构造方法

LinkedList有两个构造方法,一个是空参的构造方法,另一个参数是集合,将集合中的元素添加到队列中。

public LinkedList() {
}

public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}

6.5 工具方法

在LinkedList中,定义了一些工具方法,方便对链表进行操作。

// 将e构成新节点,添加到succ节点前面
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
// 链接前面的链表
if (pred == null)
// 如果前面没有节点,first指向新节点
first = newNode;
else
// 否则,前一个节点的next指向新节点
pred.next = newNode;
size++;
modCount++;
}

// 将e构成新节点,添加到链表头
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指向新节点
last = newNode;
else
// 否则,后面节点的prev指向新节点
f.prev = newNode;
size++;
modCount++;
}

// 将e构成新节点,添加到链表末尾
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
// 链接前面的链表
if (l == null)
// 如果前面没有节点,first指向新节点
first = newNode;
else
// 否则,前一个节点的next指向新节点
l.next = newNode;
size++;
modCount++;
}


// x节点脱离链表(删除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) {
// 如果x是第一个节点
first = next;
} else {
prev.next = next;
x.prev = null;
}
// 修改后一个节点的指向
if (next == null) {
// 如果x是最后一个节点
last = prev;
} else {
next.prev = prev;
x.next = null;
}
// 置空,方便GC
x.item = null;
size--;
modCount++;
return element;
}

// 头节点脱离链表(删除头节点,返回头节点的元素值)
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
// 修改first指向
first = next;
// 修改下一个节点指向
if (next == null)
// 如果下一个节点为空
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}

// 尾节点脱离链表(删除尾节点,返回头尾节点的元素值)
private E unlinkLast(Node<E> l) {
// assert l == last && l != null;
final E element = l.item;
final Node<E> prev = l.prev;
l.item = null;
l.prev = null; // help GC
// 修改last指向
last = prev;
// 修改上一个节点指向
if (prev == null)
// 如果上一个节点为空
first = null;
else
prev.next = null;
size--;
modCount++;
return elemen;
}


// 查找第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;
}
}


// 判断index合法性,查找、修改使用,不能等于size
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}

// 判断index合法性,添加使用,可以等于size
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}

// 越界信息
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}

// 检查index合法性,不合法抛出异常
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

// 检查index合法性,不合法抛出异常
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

6.6 List方法

6.6.1 添加方法

// 添加到链表末尾
public boolean add(E e) {
linkLast(e);
return true
}

// 添加到index位置
public void add(int index, E element) {
// 判断index合法性
checkPositionIndex(index);

if (index == size)
// 如果是最后一个位置,直接添加到末尾
linkLast(element);
else
// 否则,添加到第index节点前面,取代index的位置
linkBefore(element, node(index));
}

// 添加到链表末尾
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}


public boolean addAll(int index, Collection<? extends E> c) {
// 判断index合法性
checkPositionIndex(index);

// 转化为数组,返回副本,防止外部对c进行更改,从而影响链表
Object[] a = c.toArray();
int numNew = a.length;
if (numNew == 0)
return false;

// pred是插入位置的前一个节点,succ是插入位置节点
Node<E> pred, succ;
if (index == size) {
// 插入到最后
succ = null;
pred = last;
} else {
// 插入到index位置
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;
} // 执行完后pred指向添加的最后一个元素

// 将后面链表接上
if (succ == null) {
// 插入到最后,对应index == size情况,直接修改last指向
last = pred;
} else {
// 将后面的链表接上
pred.next = succ;
succ.prev = pred;
}

size += numNew;
modCount++;
return true;
}

6.6.2 删除方法

// 清空所有元素
public void clear() {
// 清除所有链接,方便GC(引用都指向null)
for (Node<E> x = first; x != null; ) {
Node<E> next = x.next;
x.item = null;
x.next = null;
x.prev = null;
x = next;
}
first = last = null;
size = 0;
modCount++;
}

// 删除第index个节点
public E remove(int index) {
checkElementIndex(index);
//node(index)这是返回第index个节点
return unlink(node(index));
}

// 删除第一个出现的o对象,没有找到返回false
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;
}

6.6.3 修改方法

public E set(int index, E element) {
checkElementIndex(index);
// 首先找到目标节点
Node<E> x = node(index);
E oldVal = x.item;
// 修改值
x.item = element;
return oldVal;
}

6.6.4 查找方法

// 判断链表中是否含有元素为o的节点
public boolean contains(Object o) {
return indexOf(o) != -1;
}

// 链表节点个数
public int size() {
return size;
}

// 获取第index节点的元素值
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}

// 从头开始找,元素值为o的索引,没找到返回-1
public int indexOf(Object o) {
int index = 0;
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}

// 从尾开始找,元素值为o的索引,没找到返回-1
public int lastIndexOf(Object o) {
int index = size;
if (o == null) {
for (Node<E> x = last; x != null; x = x.prev) {
index--;
if (x.item == null)
return index;
}
} else {
for (Node<E> x = last; x != null; x = x.prev) {
index--;
if (o.equals(x.item))
return index;
}
}
return -1;
}

// 返回数组副本
public Object[] toArray() {
Object[] result = new Object[size];
int i = 0;
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;
return result;
}

public <T> T[] toArray(T[] a) {
// 判断a的大小,不满足则利用反射创建新数组
if (a.length < size)
a = (T[])java.lang.reflect.Array.newInstance(
a.getClass().getComponentType(), size);
int i = 0;
Object[] result = a;
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;

// 如果a数组没有用完,最后一个元素置null
if (a.length > size)
a[size] = null;

return a;
}

6.7 实现Deque接口

6.7.1 添加方法

// 添加到链表头
public void addFirst(E e) {
linkFirst(e);
}
// 添加到链表尾
public void addLast(E e) {
linkLast(e);
}

// 入队
public boolean offer(E e) {
return add(e);
}
// 队头入队
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
// 队尾入队
public boolean offerLast(E e) {
addLast(e);
return true;
}

// 入栈
public void push(E e) {
addFirst(e);
}

6.7.2 删除方法

// 出队
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
// 队头出队
public E pollFirst() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
// 对尾出队
public E pollLast() {
final Node<E> l = last;
return (l == null) ? null : unlinkLast(l);
}

// 出栈
public E pop() {
return removeFirst();
}

// 删除链表头节点
public E remove() {
return removeFirst();
}

public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}

// 删除链表尾节点
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}

// 删除第一个等于o的节点
public boolean removeFirstOccurrence(Object o) {
return remove(o);
}

// 删除最后一个等于o的节点
public boolean removeLastOccurrence(Object o) {
if (o == null) {
for (Node<E> x = last; x != null; x = x.prev) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = last; x != null; x = x.prev) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}

6.7.3 查找方法

// 获取第一个元素
public E element() {
return getFirst();
}

public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}

// 获取最后一个元素
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}

// 返回但不删除第一个元素
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}

public E peekFirst() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}

// 返回但不删除最后一个元素
public E peekLast() {
final Node<E> l = last;
return (l == null) ? null : l.item;
}

七、PriorityQueue

public class PriorityQueue<E> extends AbstractQueue<E>
implements java.io.Serializable {

优先队列我们分析过它的迭代器,现在我没具体来看看它的方法。

7.1 属性

优先队列是使用平衡二叉堆-小顶堆来实现的,使用数组进行存储。队列[n]的两个子队列是队列[2n+1]和队列[2(n+1)]。

private static final long serialVersionUID = -7720805057305804111L;

// 默认初始大小为11
private static final int DEFAULT_INITIAL_CAPACITY = 11;

// 最大数组数量 为 Integer.MAX_VALUE - 8
// 有的虚拟机实现,数组对象的头部会占用这8个字节,在Collection那篇文章讲过
// 试图分配更大的数组可能会导致OutOfMemoryError:请求的数组大小超过VM限制
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

// 优先队列按照comparator排序,或者如果comparator为null,则按照元素的自然顺序排序:
// 对于堆中的每个节点n和n的每个后代d,n <= d。假设队列非空,则值最低的元素位于队列[0]。
transient Object[] queue; // 非私有以简化嵌套类访问

private int size = 0;

// 如果优先队列使用元素的自然循序,则为空
private final Comparator<? super E> comparator;

transient int modCount = 0; // 非私有以简化嵌套类访问

我们发现PriorityQueue同样实现了Serializable接口,并且生命其数组属性是transient,我们想到了之前分析的ArrayList,他有两个方法来实现序列化和反序列化。

7.2 构造方法

相比较于其他容器,PriorityQueue的构造方法就很多了,它有七种

public PriorityQueue() {
this(DEFAULT_INITIAL_CAPACITY, null);
}

public PriorityQueue(int initialCapacity) {
this(initialCapacity, null);
}

public PriorityQueue(Comparator<? super E> comparator) {
this(DEFAULT_INITIAL_CAPACITY, comparator);
}

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];
// 设置comparator
this.comparator = comparator;
}


public PriorityQueue(Collection<? extends E> c) {
if (c instanceof SortedSet<?>) {
// c是SortedSet的情况,取出comparator,调用initElementsFromCollection
SortedSet<? extends E> ss = (SortedSet<? extends E>) c;
this.comparator = (Comparator<? super E>) ss.comparator();
initElementsFromCollection(ss);
} else if (c instanceof PriorityQueue<?>) {
// c是PriorityQueue的情况,取出comparator,调用initFromPriorityQueue
PriorityQueue<? extends E> pq = (PriorityQueue<? extends E>) c;
this.comparator = (Comparator<? super E>) pq.comparator();
initFromPriorityQueue(pq);
} else {
// 其他情况,comparator置空,调用initFromCollection
this.comparator = null;
initFromCollection(c);
}
}

// 参数是PriorityQueue
public PriorityQueue(PriorityQueue<? extends E> c) {
this.comparator = (Comparator<? super E>) c.comparator();
initFromPriorityQueue(c);
}

// 参数是SortedSet
public PriorityQueue(SortedSet<? extends E> c) {
this.comparator = (Comparator<? super E>) c.comparator();
// SortedSet已经有序了,直接初始化数组和size就好了。
initElementsFromCollection(c);
}

我们先来看一下三个init方法,总的来说,如果传入的参数是有序的(PriorityQueue、SortedSet),则转化成数组,进行赋值。

如果传入的参数是其他集合,就需要赋值后调用heapify方法,调整元素顺序,保证堆规则。

// c是SortedSet的情况
private void initElementsFromCollection(Collection<? extends E> c) {
Object[] a = c.toArray();
// If c.toArray incorrectly doesn't return Object[], copy it.
if (a.getClass() != Object[].class)
a = Arrays.copyOf(a, a.length, Object[].class);

// 如果指定的集合或其任何元素为空,抛出异常
int len = a.length;
if (len == 1 || this.comparator != null)
for (int i = 0; i < len; i++)
if (a[i] == null)
throw new NullPointerException();

// 初始化数组和size
this.queue = a;
this.size = a.length;
}

// c是PriorityQueue的情况
private void initFromPriorityQueue(PriorityQueue<? extends E> c) {
if (c.getClass() == PriorityQueue.class) {
this.queue = c.toArray();
this.size = c.size();
} else {
// 如果不是PriorityQueue,调用initFromCollection
initFromCollection(c);
}
}

// 初始化数组、size,调用heapify方法
private void initFromCollection(Collection<? extends E> c) {
initElementsFromCollection(c);
heapify();
}

// 在整个树中建立堆不变式(如上所述),不考虑调用之前元素的顺序。
private void heapify() {
// 从第一个父元素开始,自下而上进行下沉
for (int i = (size >>> 1) - 1; i >= 0; i--)
siftDown(i, (E) queue[i]);
}

7.3 重要方法

对于堆来说,下沉和上浮是保证堆规则的重要方法,下面我们看一下下沉和上浮的方法。

// 下沉操作,分为有比较器和没有比较器两种情况
// 将x插入到k的位置,通过反复将x降到树的下层,直到它小于或等于它的子元素,或者是叶子,从而保持堆不变。
private void siftDown(int k, E x) {
if (comparator != null)
siftDownUsingComparator(k, x);
else
siftDownComparable(k, x);
}

// 有比较器
private void siftDownUsingComparator(int k, E x) {
int half = size >>> 1;
while (k < half) {
// 左孩子下标
int child = (k << 1) + 1;
Object c = queue[child];
// 右孩子下标
int right = child + 1;
if (right < size &&
// compare方法注释有着这样的描述:
// 比较它的两个order参数。当第一个参数小于、等于或大于第二个参数时,返回一个负整数、零或正整数。
// 下面代码>0表示左孩子大于右孩子
comparator.compare((E) c, (E) queue[right]) > 0)
// 将右孩子下标以及右孩子保存到child和c中
c = queue[child = right];

// 执行到这里,c保存的是k的左右孩子中元素值较小的那个,child保存的是他的下标
// 再和x进行比较,如果x更小,则跳出循环,否则说明还有下沉的可能,继续循环
if (comparator.compare(x, (E) c) <= 0)
break;
// 进行下沉操作
// 将较小的值放到k的位置
queue[k] = c;
// 更新k到下一层
k = child;
}
// 执行到这里,k是当前坐标,并且当前元素的左右孩子都比x大
// 将x插入到k位置
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 &&
// compareTo方法注释有着这样的描述:将此对象与指定的order对象进行比较。
// 返回一个负整数、零或正整数,因为该对象小于、等于或大于指定的对象。
// 和上面的方法基本相同,只是比较方法使用的是元素的compareTo方法
((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;
}

/**************************************************************************************/

// 上浮,分为有比较器和没有比较器两种情况
// 在k位置插入x,通过向上提升x直到它大于或等于其父节点,或者是根节点,来保持堆不变。
private void siftUp(int k, E x) {
if (comparator != null)
siftUpUsingComparator(k, x);
else
siftUpComparable(k, x);
}

// 有比较器
private void siftUpUsingComparator(int k, E x) {
while (k > 0) {
// 父节点下标、元素
int parent = (k - 1) >>> 1;
Object e = queue[parent];
// 父元素和x比较,如果父元素小,则说明上浮到顶,退出
if (comparator.compare(x, (E) e) >= 0)
break;
// 进行上浮操作
// 将父元素放到k的位置
queue[k] = e;
// 更新k到上一层
k = parent;
}
queue[k] = x;
}

// 没有比较器
private void siftUpComparable(int k, E 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;
}

7.4 工具方法

// 返回比较器
public Comparator<? super E> comparator() {
return comparator;
}

// 删除o元素,失败返回false
boolean removeEq(Object o) {
for (int i = 0; i < size; i++) {
if (o == queue[i]) {
removeAt(i);
return true;
}
}
return false;
}

// 删除第i个元素,将最后一个元素放置到i的位置
// 如果上浮了,返回这个元素,否则返回null。
// 在迭代器那篇文章中,我们使用了这个函数的返回值。
private E removeAt(int i) {
// assert i >= 0 && i < size;
modCount++;
int s = --size;
if (s == i) // removed last element
queue[i] = null;
else {
E moved = (E) queue[s];
queue[s] = null;
// 将最后一个元素插入第i个位置,下沉
siftDown(i, moved);
if (queue[i] == moved) {
// 说明最后一个元素比i位置下面的元素都小,则上浮
siftUp(i, moved);
if (queue[i] != moved)
// 说明上浮了,返回这个元素
return moved;
}
}
return null;
}

// 返回o元素的索引,没有返回-1
private int indexOf(Object o) {
if (o != null) {
for (int i = 0; i < size; i++)
if (o.equals(queue[i]))
return i;
}
return -1;
}

// 扩容
private void grow(int minCapacity) {
int oldCapacity = queue.length;
// Double size if small; else grow by 50%
// 如果小于64,扩容二倍+2.
// 如果大于等于64,扩容1.5倍
int newCapacity = oldCapacity + ((oldCapacity < 64) ?
(oldCapacity + 2) :
(oldCapacity >> 1));
// overflow-conscious code
// 如果大于MAX_ARRAY_SIZE,则修改为Integer.MAX_VALUE
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 扩容,我们在ArrayList的文章中分析了这个操作
queue = Arrays.copyOf(queue, newCapacity);
}

// 确保最大数据大小
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
// 最大不能超过Integer.MAX_VALUE
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}

7.5 集合方法

// 查询优先队列中是否含有o元素
public boolean contains(Object o) {
return indexOf(o) != -1;
}
// 移除o元素
public boolean remove(Object o) {
int i = indexOf(o);
if (i == -1)
return false;
else {
removeAt(i);
return true;
}
}

public Object[] toArray() {
return Arrays.copyOf(queue, size);
}

public <T> T[] toArray(T[] a) {
final int size = this.size;
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(queue, size, a.getClass());
System.arraycopy(queue, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
// 返回size
public int size() {
return size;
}

7.6 队列方法

// 添加元素
public boolean add(E e) {
return offer(e);
}

// 清空所有元素
public void clear() {
modCount++;
for (int i = 0; i < size; i++)
queue[i] = null;
size = 0;
}

// 入队
public boolean offer(E e) {
if (e == null)
throw new NullPointerException();
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;
}

// 查看队头元素
public E peek() {
return (size == 0) ? null : (E) queue[0];
}

// 出队
public E poll() {
if (size == 0)
return null;
int s = --size;
modCount++;

E result = (E) queue[0];
E x = (E) queue[s];
queue[s] = null;
// 将最后一个元素从顶向下沉
if (s != 0)
siftDown(0, x);
return result;
}

八、Map接口

我们首先看一下Map接口的定义。

/**
* Map是一个将key(键)映射到value(值)的对象。Map不能包含重复的key,每个key最多可以映射到一个value。
*
* 这个接口代替了Dictionary类, Dictionary类是一个完全抽象类,而不是接口。
*
* Map接口提供了三个集合视图, 他们允许将Map的内容视为
* key的set集合,value的collection集合,key-value的set集合
* 映射的顺序定义为映射集合视图上的迭代器返回其元素的顺序。
* 一些Map的实现,例如TreeMap,对他们的顺序做了特定的保证,其他的类,例如HashMap,则没有。
*
* 注意:要格外小心使用可变对象作为key
* 如果使用equals比较的方式改变了一个对象的值,而对象是Map的一个key,则不规定Map的行为(没太懂啥意思。。)
* 这种禁止的一种特殊情况是,不允许Map将自己作为key。
* 虽然map可以将自己作为value,但是要特别小心:equals和hashCode方法在这样的map上将不能被很好的定义。
*
* 所有的map实现类都应该提供两个“标准”构造函数:
* 一个空参构造函数,创建一个空的映射
* 另一个有一个Map类型的单一参数,创建一个新的,和参数同样的key-value映射的Map
* 事实上,后一个构造函数允许用户复制任何Map,生成所需的Map。
* 没有办法强制执行这个建议(因为接口不能包含构造函数),但是JDK中的所有通用Map实现都遵守这个建议。
*
* 此接口中的“破坏性”方法(即修改Map的方法)在该Map不支持操作时抛出UnsupportedOperationException。
* 如果是这种情况,调用对Map没有影响,这些方法可能会抛出UnsupportedOperationException。
* 例如,在不可修改的Map上调用putAll(Map)方法时,如果要“叠加”(添加)的Map为空,可能会抛出异常。
*
* 一些Map实现对它们可能包含的key和value有限制。例如,有些实现禁止key和value为空,有些实现限制key的类型。
* 试图插入不符合条件的key或value会引发未检查异常,通常是NullPointerException或ClassCastException。
* 试图查询不符合条件的key或value是否存在可能会引发异常,或者只返回false。
* 更概括地说,尝试对不符合条件的key或value进行操作,操作完成不会将不符合条件的元素插入到Map中
* 这可能会引发异常,也可能会成功(取决于实现的选项)。此类异常在此接口的规范中被标记为“可选”。
*
* 集合框架接口中的许多方法都是根据equals方法定义的。
* 例如,containsKey(Object key)方法的规范说:
* “当且仅当此Map包含键k的映射(key==null ? k==null:key.equals(k))。”
* 本规范不应被解释为调用Map.containsKey,传入非空参数key会导致key.equals(k)被任意键k调用。
* 实现可以自由地实现优化,从而避免了equals调用,
* 例如,首先比较两个键的哈希码。(Object.hashCode()规范保证两个哈希码不相等的对象不能相等。)
* 更一般地说,各种集合框架接口的实现可以在实现者认为合适的地方自由地利用底层Object方法的指定行为。
*
* 一些执行Map递归遍历的操作可能会失败,由于Map直接或间接包含自身实例的异常。
* 这包括clone()、equals()、hashCode()和toString()方法。
* 实现可以有选择地处理自引用场景,但是大多数当前的实现并不这样做。
*
* 该接口是Java集合框架的成员。
*/
public interface Map<K,V> {

8.1 Entry接口

Entry接口是Map接口的子接口,他抽象了数据存储的节点,定义了一些操作数据的基本方法。

/**
* 一个Map的项(键值对)Map.entrySet方法返回一个Map的集合视图,其元素就是这个类。
* 获取Map entry引用的方法来自这个集合视图的迭代器。
* 这些Map.Entry对象只在迭代期间有效
* 更正式的说法是,如果在迭代器返回entry之后对更改了map,则map entry的行为没有定义
* 除非通过setValue方法操作map entry。
*/
interface Entry<K,V> {
/**
* 返回与此项对应的键
* @throws IllegalStateException 如果entry被移除,实现可能抛出这个异常
*/
K getKey();

/**
* 返回与此项对应的值。如果映射已经从map中删除(被迭代器的remove操作),则此调用结果是未定义的。
* @throws IllegalStateException 如果entry被移除,实现可能抛出这个异常
*/
V getValue();

/**
* 用特定的value替换这个entry对应的value。
* (写进map。)如果映射已经从map删除(被迭代器的remove操作),则这个调用的行为未定义。
* 返回旧的value
* @throws UnsupportedOperationException 如果map不支持put操作
* @throws ClassCastException 如果特定值的类阻止其存储到map中
* @throws NullPointerException 如果map不允许value为null,而特定value为null
* @throws IllegalArgumentException 如果value的某些属性阻止将其存储在map中
* @throws IllegalStateException 如果entry被移除,实现可能抛出这个异常
*/
V setValue(V value);

/**
* 将指定的对象与此entry进行相等性比较。
* 如果给定的对象也是一个map entry,且两个entry表示相同的映射,则返回true。
* 更正式的说,两个entry e1 和 e2 表示相同的map,如果:
* (e1.getKey()==null ? e2.getKey()==null : e1.getKey().equals(e2.getKey()))
* &&
* (e1.getValue()==null ? e2.getValue()==null : e1.getValue().equals(e2.getValue()))
* 这将确保equals方法在Map.Entry接口的不同实现之间正常工作。
*/
boolean equals(Object o);

/**
* 返回map entry的hashCode。map entry的hashCode定义如下:
* (e.getKey()==null ? 0 : e.getKey().hashCode())
* ^
* (e.getValue()==null ? 0 : e.getValue().hashCode())
* 这将确保对于任意两个entry e1和e2,如果e1.equals(e2)则e1.hashCode()==e2.hashCode()
* 这是Object.hashCode的一般约定要求的。
*/
int hashCode();

8.2 方法

/**
* 返回此映射中的键值映射的数目。
* 如果映射包含大于Integer.MAX_VALUE元素,返回Integer.MAX_VALUE。
*/
int size();

/**
* 如果map不包含key-value映射,返回true
*/
boolean isEmpty();

/**
* 如果此映射包含指定键的映射,则返回true。
* 更正式地说,当且仅当此map包含一个key k,使得(key==null ? k == null: key.equals(k)),返回true
*
* @throws ClassCastException 如果键的类型不适合此Map
* @throws NullPointerException 如果指定的key为null,并且此Map不允许key为null
*/
boolean containsKey(Object key);

/**
* 如果此Map将一个或多个key映射到指定的value,则返回true。
* 更正式地说,当且仅当此Map包含至少一个到值v的映射
* 使得(value==null ? v == null: value.equals (v)) 则返回true。
* 对于Map接口的大多数实现,此操作可能需要线性时间。
*
* @throws ClassCastException 如果value的类型不适合此Map
* @throws NullPointerException 如果指定的value为null,并且此Map不允许null
*/
boolean containsValue(Object value);

/**
* 返回指定key映射到的value,如果此Map不包含key的映射,则返回null。
*
* 更正式地说,如果这个Map包含一个从key k到value v的映射,
* 使得(key==null ? k==null: key.equals(k)),则该方法返回v;
* 否则返回null。(最多可以有一个这样的映射。)
*
* 如果此Map允许空值,则null的返回值不一定表示该映射不包含键的映射;
* 也有可能映射显式地将键映射到null。
* 可以使用containsKey操作来区分这两种情况。
*
* @throws ClassCastException 如果value的类型不适合此Map
* @throws NullPointerException 如果指定的key为null,并且此Map不允许null
*/
V get(Object key);

// Modification Operations 修改操作

/**
* 将指定value与此Map中的指定key关联(可选操作)。
* 如果Map以前包含key的映射,则旧值将被指定的value替换
* (当且仅当m.containsKey(k)返回true时,一个map m才被认为包含了一个key k的映射。)
*
* @return 返回前一个与key关联的值,或者如果key没有映射的话,返回null。
* (如果实现支持null值,返回null也可以表明前一个与key关联的是)
* @throws UnsupportedOperationException 如果此Map不支持put操作
* @throws ClassCastException 如果指定的key或value的类阻止它存储在此Map中
* @throws NullPointerException 如果指定的value或value为null,并且此Map不允许null
* @throws IllegalArgumentException 如果指定的key或value的某些属性阻止将其存储在此Map中
*/
V put(K key, V value);

/**
* 如果key存在,则从该映射中删除该key的映射(可选操作)。
* 更正式地说,如果这个映射包含一个从key k到value v的映射,使得(key==null ?k==null: key.equals(k))
* 该映射被删除。(Map最多可以包含一个这样的映射。)
*
* 返回此映射以前与key关联的value,或者null(如果映射不包含key的映射)。
*
* 如果此映射允许空值,则null的返回值不一定表示该Map不包含key的映射;
* 也有可能映射显式地将key映射到null。
*
* 一旦调用返回,Map将不包含指定key的映射。
*
* @throws UnsupportedOperationException 如果此Map不支持remove操作
* @throws ClassCastException 如果key的类型不适合此Map
* @throws NullPointerException 如果指定的key为null,并且此Map不允许null
*/
V remove(Object key);


// Bulk Operations 批量操作

/**
* 将指定Map的所有映射复制到此Map(可选操作)。
* 相当于在对于从key k到指定Map中的value v的每个映射上调用一次put(k, v)。
* 如果在操作过程中修改了指定的Map,则此操作的行为未定义。
*
* @throws UnsupportedOperationException 如果此Map不支持putAll操作
* @throws ClassCastException 如果指定Map中的key或value的类阻止将其存储在此Map中
* @throws NullPointerException 如果指定的map为null,或者指定的Map包含的key或value为null,而Map不允许
* null
* @throws IllegalArgumentException 如果指定Map中的key或value的某些属性阻止将其存储在此Map中
*/
void putAll(Map<? extends K, ? extends V> m);

/**
* 从该Map中删除所有映射(可选操作)。这个调用返回后,Map将为空。
*
* @throws UnsupportedOperationException 如果此Map不支持clear操作
*/
void clear();


// Views 视图,三种,Set->key Collection->value Set->entry

/**
* 返回此映射中包含的key的Set视图。
* Set由Map支持,因此对Map的更改将反映在Set中,反之亦然。
* 如果在对Set进行迭代时修改了Map(除了通过迭代器自己的删除操作),则迭代的结果是未定义的。
* Set支持删除元素,它通过Iterator.remove,Set.remove,removeAll,retainAll和clear操作
* 从Map中删除对应的映射。它不支持add或addAll操作。
*/
Set<K> keySet();

/**
* 返回此Map中包含的value的Collection视图。
* Collection由Map支持,因此对Map的更改反映在Collection中,反之亦然。
* 如果在Collection的迭代过程中修改了Map(除了通过迭代器自己的删除操作),则迭代的结果是未定义的。
* Collection支持元素删除,它通过Iterator.remove,Collection.remove,removeAll,retainAll和clear操作
* 从Map中删除对应的映射。它不支持add或addAll操作。
*/
Collection<V> values();

/**
* 返回此Map中包含的entry的Set视图。
* Set由Map支持,因此对Map的更改将反映在Set中,反之亦然。
* 如果在对Set进行迭代时修改了Map
* (除了通过迭代器自己的删除操作,或者通过迭代器返回的entry的setValue操作),迭代的结果是未定义的。
* Set支持元素删除,它通过Iterator.remove, Set.remove, removeAll, retainAll和clear操作
* 从Map中删除对应的映射。它不支持add或addAll操作。
*/
Set<Map.Entry<K, V>> entrySet();

8.3 AbstractMap实现类

8.3.1 SimpleEntry

/**
* 一个包含key和value的entry。可以使用setValue方法更改value值。
* 该类简化了构建自定义Map实现的过程。
* 例如,在Map.entrySet().toArray方法中返回SimpleEntry数组可能比较方便。
*
* 其方法都是按照Entry接口实现的,比较简单,大家简单看一下就好。
*/
public static class SimpleEntry<K,V>
implements Entry<K,V>, java.io.Serializable
{
private static final long serialVersionUID = -8499721149061103585L;

private final K key;
private V value;

public SimpleEntry(K key, V value) {
this.key = key;
this.value = value;
}

public SimpleEntry(Entry<? extends K, ? extends V> entry) {
this.key = entry.getKey();
this.value = entry.getValue();
}

public K getKey() {
return key;
}

public V getValue() {
return value;
}

public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue;
}


public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
return eq(key, e.getKey()) && eq(value, e.getValue());
}

public int hashCode() {
return (key == null ? 0 : key.hashCode()) ^
(value == null ? 0 : value.hashCode());
}

public String toString() {
return key + "=" + value;
}

}

8.3.2 SimpleImmutableEntry

/**
* 一个保持不变key和value的entry。
* 这个类不支持setValue方法。这个类在返回键值映射的线程安全快照的方法中可能很方便。
*
* 同样比较简单,大家看一下就好
*/
public static class SimpleImmutableEntry<K,V>
implements Entry<K,V>, java.io.Serializable
{
private static final long serialVersionUID = 7138329143949025153L;

private final K key;
private final V value;

public SimpleImmutableEntry(K key, V value) {
this.key = key;
this.value = value;
}

public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry) {
this.key = entry.getKey();
this.value = entry.getValue();
}

public K getKey() {
return key;
}

public V getValue() {
return value;
}

// 不支持setValue方法
public V setValue(V value) {
throw new UnsupportedOperationException();
}

public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
return eq(key, e.getKey()) && eq(value, e.getValue());
}

public int hashCode() {
return (key == null ? 0 : key.hashCode()) ^
(value == null ? 0 : value.hashCode());
}

public String toString() {
return key + "=" + value;
}
}

8.3.3 方法

public boolean containsValue(Object value) {
// 获取迭代器
Iterator<Entry<K,V>> i = entrySet().iterator();
if (value==null) {
// 查找null的情况
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (e.getValue()==null)
return true;
}
} else {
// 查找value非空的情况
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (value.equals(e.getValue()))
return true;
}
}
return false;
}

// 和上面的方法类似
public boolean containsKey(Object key) {
Iterator<Map.Entry<K,V>> i = entrySet().iterator();
if (key==null) {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (e.getKey()==null)
return true;
}
} else {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (key.equals(e.getKey()))
return true;
}
}
return false;
}

// 同样通过迭代器查找
public V get(Object key) {
Iterator<Entry<K,V>> i = entrySet().iterator();
if (key==null) {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (e.getKey()==null)
return e.getValue();
}
} else {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (key.equals(e.getKey()))
return e.getValue();
}
}
return null;
}


// Modification Operations

// 不支持put方法
public V put(K key, V value) {
throw new UnsupportedOperationException();
}

public V remove(Object key) {
// 首先通过迭代器找目标entry,找到或遍历结束退出循环
Iterator<Entry<K,V>> i = entrySet().iterator();
Entry<K,V> correctEntry = null;
if (key==null) {
while (correctEntry==null && i.hasNext()) {
Entry<K,V> e = i.next();
if (e.getKey()==null)
correctEntry = e;
}
} else {
while (correctEntry==null && i.hasNext()) {
Entry<K,V> e = i.next();
if (key.equals(e.getKey()))
correctEntry = e;
}
}

// 如果找到目标entry,调用迭代器remove方法移除
V oldValue = null;
if (correctEntry !=null) {
oldValue = correctEntry.getValue();
i.remove();
}
// 返回删除entry的value
return oldValue;
}

// Bulk Operations

// 使用foreach遍历entrySet,调用put方法
public void putAll(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
}

// 使用entrySet的clear方法
public void clear() {
entrySet().clear();
}


// Views 视图,三种,Set->key Collection->value Set->entry

transient Set<K> keySet;
transient Collection<V> values;

public Set<K> keySet() {
Set<K> ks = keySet;
if (ks == null) {
// 创建一个匿名类,重写AbstractSet方法
// 方法都是调用AbstractMap的相关方法
ks = new AbstractSet<K>() {
public Iterator<K> iterator() {
return new Iterator<K>() {
private Iterator<Entry<K,V>> i = entrySet().iterator();

public boolean hasNext() {
return i.hasNext();
}

public K next() {
return i.next().getKey();
}

public void remove() {
i.remove();
}
};
}

public int size() {
return AbstractMap.this.size();
}

public boolean isEmpty() {
return AbstractMap.this.isEmpty();
}

public void clear() {
AbstractMap.this.clear();
}

public boolean contains(Object k) {
return AbstractMap.this.containsKey(k);
}
};
keySet = ks;
}
return ks;
}

public Collection<V> values() {
Collection<V> vals = values;
if (vals == null) {
// 创建一个匿名类,重写AbstractCollection方法
// 方法都是调用AbstractMap的相关方法
vals = new AbstractCollection<V>() {
public Iterator<V> iterator() {
return new Iterator<V>() {
private Iterator<Entry<K,V>> i = entrySet().iterator();

public boolean hasNext() {
return i.hasNext();
}

public V next() {
return i.next().getValue();
}

public void remove() {
i.remove();
}
};
}

public int size() {
return AbstractMap.this.size();
}

public boolean isEmpty() {
return AbstractMap.this.isEmpty();
}

public void clear() {
AbstractMap.this.clear();
}

public boolean contains(Object v) {
return AbstractMap.this.containsValue(v);
}
};
values = vals;
}
return vals;
}

// 抽象方法
public abstract Set<Entry<K,V>> entrySet();


// Comparison and hashing

public boolean equals(Object o) {
if (o == this)
return true;

if (!(o instanceof Map))
return false;
Map<?,?> m = (Map<?,?>) o;
if (m.size() != size())
return false;

try {
// 使用迭代器遍历entrySet,判断对于相同的key,二者的value是否相等
Iterator<Entry<K,V>> i = entrySet().iterator();
while (i.hasNext()) {
Entry<K,V> e = i.next();
K key = e.getKey();
V value = e.getValue();
if (value == null) {
if (!(m.get(key)==null && m.containsKey(key)))
return false;
} else {
if (!value.equals(m.get(key)))
return false;
}
}
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}

return true;
}

// 所有entry的hashCode和
public int hashCode() {
int h = 0;
Iterator<Entry<K,V>> i = entrySet().iterator();
while (i.hasNext())
h += i.next().hashCode();
return h;
}

public String toString() {
Iterator<Entry<K,V>> i = entrySet().iterator();
if (! i.hasNext())
return "{}";

StringBuilder sb = new StringBuilder();
sb.append('{');
for (;;) {
Entry<K,V> e = i.next();
K key = e.getKey();
V value = e.getValue();
sb.append(key == this ? "(this Map)" : key);
sb.append('=');
sb.append(value == this ? "(this Map)" : value);
if (! i.hasNext())
return sb.append('}').toString();
sb.append(',').append(' ');
}
}

protected Object clone() throws CloneNotSupportedException {
AbstractMap<?,?> result = (AbstractMap<?,?>)super.clone();
result.keySet = null;
result.values = null;
return result;
}

private static boolean eq(Object o1, Object o2) {
return o1 == null ? o2 == null : o1.equals(o2);
}

九、HashMap

9.1 概述

我们首先看一下HashMap定义,它继承了AbstractMap抽象类,实现了Map、Cloneable、Serializable接口。

public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {

在这里插入图片描述

翻译如下:

基于哈希表的Map接口实现。此实现提供所有可选的Map操作,并允许空值和空键。(HashMap类大致相当于Hashtable,只是它是不同步的,并且允许为空。)该类不保证Map的顺序;特别是,它不能保证顺序在一段时间内保持不变。

这个实现为基本操作(get和put)提供了稳定的时间性能,假设散列函数将元素适当地分散到各个桶中。集合视图的迭代需要与HashMap实例的“容量”(桶的数量)及其大小(键值映射的数量)成比例的时间。因此,如果迭代性能很重要,那么不要将初始容量设置得太高(或负载因子太低)是非常重要的。

HashMap实例有两个影响其性能的参数:初始容量和负载因子。容量是哈希表中的桶数,初始容量只是创建哈希表时的容量。负载因子是一个度量哈希表在其容量自动增加之前允许的满度的度量。当哈希表中的entry数超过负载因子和当前容量的乘积时,将对哈希表进行rehash(即重新构建内部数据结构),使哈希表的桶数大约扩大两倍。

一般来说,默认的负载因子(0.75)在时间和空间成本之间提供了很好的权衡。更高的值减少了空间开销,但增加了查找成本(反映在HashMap类的大多数操作中,包括get和put)。在设置map的初始容量时,应该考虑map中entry的期望数量及其负载因子,从而最小化rehash操作的频率。如果初始容量大于entry的最大数量除以负载因子,则不会发生rehash操作。

如果要将许多映射存储在HashMap实例中,那么使用足够大的容量创建映射将比根据需要执行自动rehash来扩展表更有效地存储映射。注意,使用具有相同hashCode()的多个键肯定会降低散列表的性能。为了改善影响,当键是可比较的,这个类可以使用键之间的比较顺序来帮助打破联系。

注意,这个实现不是同步的。如果多个线程同时访问一个hash map,并且至少有一个线程从结构上修改了该Map,则必须在外部对其进行同步。(结构修改是指增加或删除一个或多个映射的操作;仅仅更改与一个实例已经包含的键相关联的值并不是结构修改。)这通常是通过对一些自然封装了map对象进行同步来实现的。如果不存在这样的对象,则应该使用Collections.synchronizedMap方法“包装”Map。这最好在创建时完成,以防止意外的不同步访问Map:

Map m = Collections.synchronizedMap(new HashMap(…));

这个类的所有“集合视图方法”返回的迭代器是fail-fast的:如果在创建迭代器之后的任何时候,以任何方式(除了通过迭代器自己的删除方法)对映射进行结构修改,迭代器将抛出ConcurrentModificationException异常。因此,在面对并发修改时,迭代器会快速而干净地失败,而不是在将来某个不确定的时间冒任意的、不确定的行为的风险。

注意,不能保证迭代器的fail-fast行为,因为通常来说,在存在非同步并发修改的情况下,不可能做出任何严格的保证。fail-fast迭代器在最大努力的基础上抛出ConcurrentModificationException。因此,编写一个依赖于这个异常的正确性的程序是错误的:迭代器的快速故障行为应该只用于检测bug。

该类是Java集合框架的成员。

9.2 属性

// 默认的初始容量-必须是2的幂。
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

// 最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;

// 在构造函数中没有指定时使用的负载因子。
static final float DEFAULT_LOAD_FACTOR = 0.75f;

// 使用红黑树的桶计数阈值。当向至少有这么多节点的桶中添加元素时,该桶将被转换为红黑树。
// 该值必须大于2,并且应该至少为8,以便在收缩时转换回普通桶。
static final int TREEIFY_THRESHOLD = 8;

// 在resize操作时,红黑树退化为链表的桶计数临界值
static final int UNTREEIFY_THRESHOLD = 6;

// 是在转变成树之前,还会有一次判断,只有键值对数量大于64才会发生转换。
// 这是为了避免在哈希表建立初期,多个键值对恰好被放入了同一个链表中而导致不必要的转化。
static final int MIN_TREEIFY_CAPACITY = 64;

/* ---------------- Fields -------------- */

// 数组,也就是我们说的桶
transient Node<K,V>[] table;

// 缓存entrySet
transient Set<Map.Entry<K,V>> entrySet;

// 键值对数量
transient int size;

// 记录操作数,防止并发修改,支持fail-fast
transient int modCount;

// 要调整的下一个大小值(容量 * 负载因子)。
// 如果还没有分配表数组,则此字段保留初始数组容量,或为零,表示DEFAULT_INITIAL_CAPACITY
int threshold;

// 加载因子
final float loadFactor;

9.3 存储结构

  • HashMap使用的是子类Node来存储数据,实现了Map.Entry接口。

  • 存储数量较大,满足树化条件时,TreeNode<K,V>为其HashMap的存储节点结构。

static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;

Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}

public final K getKey() { return key; }
public final V getValue() { return value; }
public final String toString() { return key + "=" + value; }

public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}

public final V setValue(V newValue) {
......
}

public final boolean equals(Object o) {
......
}
}
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
TreeNode<K,V> parent; // red-black tree links
TreeNode<K,V> left;
TreeNode<K,V> right;
TreeNode<K,V> prev; // needed to unlink next upon deletion
boolean red;
TreeNode(int hash, K key, V val, Node<K,V> next) {
super(hash, key, val, next);
}

final TreeNode<K,V> root() {
for (TreeNode<K,V> r = this, p;;) {
if ((p = r.parent) == null)
return r;
r = p;
}
}
}

9.4 三种试图

可以理解成三个维度分析HashMap,都是对HashMap的封装

public Set<K> keySet() {
Set<K> ks = keySet;
if (ks == null) {
ks = new KeySet();
keySet = ks;
}
return ks;
}

final class KeySet extends AbstractSet<K> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator<K> iterator() { return new KeyIterator(); }
public final boolean contains(Object o) { return containsKey(o); }
public final boolean remove(Object key) {
return removeNode(hash(key), key, null, false, true) != null;
}
}

/*****************************************************************************************************/

public Collection<V> values() {
Collection<V> vs = values;
if (vs == null) {
vs = new Values();
values = vs;
}
return vs;
}

final class Values extends AbstractCollection<V> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator<V> iterator() { return new ValueIterator(); }
public final boolean contains(Object o) { return containsValue(o); }
}

/*****************************************************************************************************/
//把Key,Value放到一起
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}

final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator<Map.Entry<K,V>> iterator() {
return new EntryIterator();
}
public final boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Node<K,V> candidate = getNode(hash(key), key);
return candidate != null && candidate.equals(e);
}
public final boolean remove(Object o) {
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Object value = e.getValue();
return removeNode(hash(key), key, value, true, true) != null;
}
}
}

9.5 基本方法

9.5.1 构造方法

public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}

public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}

public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}

public HashMap(Map<? extends K, ? extends V> m) {
this.loadFactor = DEFAULT_LOAD_FACTOR;
putMapEntries(m, false);
}

9.5.2 Map方法

可以看到,这些Map方法调用了HashMap的一些基础实现,getNode方法、putVal方法、removeNode方法、putMapEntries方法。

public int size() {
return size;
}

public boolean isEmpty() {
return size == 0;
}

// 调用getNode方法
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}

public boolean containsKey(Object key) {
return getNode(hash(key), key) != null;
}

// 调用putVal方法
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}

// 调用putMapEntries方法
public void putAll(Map<? extends K, ? extends V> m) {
putMapEntries(m, true);
}

// 调用removeNode方法
public V remove(Object key) {
Node<K,V> e;
return (e = removeNode(hash(key), key, null, false, true)) == null ?
null : e.value;
}

public void clear() {
Node<K,V>[] tab;
modCount++;
if ((tab = table) != null && size > 0) {
size = 0;
// 清空所有桶
for (int i = 0; i < tab.length; ++i)
tab[i] = null;
}
}

public boolean containsValue(Object value) {
Node<K,V>[] tab; V v;
if ((tab = table) != null && size > 0) {
// 遍历所有桶
for (int i = 0; i < tab.length; ++i) {
// 遍历每一个桶
for (Node<K,V> e = tab[i]; e != null; e = e.next) {
if ((v = e.value) == value ||
(value != null && value.equals(v)))
return true;
}
}
}
return false;
}

上面我们看到,Map方法的实现中,调用了getNode方法、putVal方法、removeNode方法、putMapEntries方法,下面我们就看一下这些方法的实现。

9.5.3 增删改查

// hash key的哈希值
// onlyIfAbsent 如果为true,不改变现有值
// evict 如果为false,处于创建模式
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
// 如果还没有初始化table,调用resize方法,这个方法后面再说
n = (tab = resize()).length;
// 到这里,tab == table n == tab.length
// (n - 1) & hash 散列(与n-1进行与运算进行取余操作,散列到位置)
if ((p = tab[i = (n - 1) & hash]) == null)
// 如果桶是空的,将当前键值对添加进去
tab[i] = newNode(hash, key, value, null);
// 到这里,p指向当前hash桶的第一个Node
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
// hash值相等,并且key相等,说明要执行更改操作,保存p节点
e = p;
else if (p instanceof TreeNode)
// 如果p是TreeNode,当前为红黑树结构,调用TreeNode的putTreeVal方法
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
// 链表结构、遍历当前桶中的链表
for (int binCount = 0; ; ++binCount) {
// 找到最后一个节点
if ((e = p.next) == null) {
// 添加到链表最后
p.next = newNode(hash, key, value, null);
// 如果链表长度大于TREEIFY_THRESHOLD,调用treeifyBin方法转化为红黑树
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
// 如果发现hash值相等,并且key相等,说明要执行更改操作
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
// 说明要执行修改操作,e为要修改的Node
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
// 返回旧值
return oldValue;
}
}
// 到这里,说明执行的是添加操作
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}


final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
int s = m.size();
if (s > 0) {
if (table == null) { // pre-size
// 如果table还没有初始化
float ft = ((float)s / loadFactor) + 1.0F;
int t = ((ft < (float)MAXIMUM_CAPACITY) ?
(int)ft : MAXIMUM_CAPACITY);
if (t > threshold)
threshold = tableSizeFor(t);
}
else if (s > threshold)
// 扩容table为2倍
resize();
// 遍历m,添加
for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
K key = e.getKey();
V value = e.getValue();
putVal(hash(key), key, value, false, evict);
}
}
}

总结一下putVal方法:

if 还没有初始化table
调用resize方法初始化table
else if 当前桶是空的
将当前键值对添加进去
else
if 要执行修改操作
保存要修改的节点
else if 当前桶中已经是红黑树
执行TreeNode的putTreeVal方法
else
for
if 到链表末尾
将当前节点添加到链表末尾
如果链表长度超过TREEIFY_THRESHOLD,转化为红黑树
要执行修改操作
保存要修改的节点
如果保存了要修改的节点
修改操作,返回旧值,end

说明执行了添加操作,更新相关值
返回null,end

删除方法:

// matchValue 如果为true,仅在value相等时删除
// movable 如果为false,则在删除时不移动其他节点
final Node<K,V> removeNode(int hash, Object key, Object value,
boolean matchValue, boolean movable) {
Node<K,V>[] tab; Node<K,V> p; int n, index;
if ((tab = table) != null && (n = tab.length) > 0 &&
// 对应的桶为空说明要删除的键值对肯定不存在,直接结束,否则,继续查找
(p = tab[index = (n - 1) & hash]) != null) {
Node<K,V> node = null, e; K k; V v;
// 到这里 p为桶里第一个节点,tab为table n为tab.length
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
// 第一个节点就是要删除的节点,保存p到node
node = p;
else if ((e = p.next) != null) {
if (p instanceof TreeNode)
// 桶中是红黑树,调用TreeNode的getTreeNode方法获取节点,保存到node
node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
else {
// 遍历链表,找到要删除的节点,保存到node
do {
if (e.hash == hash &&
((k = e.key) == key ||(key != null && key.equals(k)))) {
node = e;
break;
}
p = e;
} while ((e = e.next) != null);
}
}
// node为要删除的节点
// 或者不需要matchValue,或者value相等,才删除
if (node != null && (!matchValue ||
(v = node.value) == value ||(value != null && value.equals(v)))) {
if (node instanceof TreeNode)
// 删除树节点
((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
else if (node == p)
// 删除桶的第一个节点
tab[index] = node.next;
else
p.next = node.next;
++modCount;
--size;
afterNodeRemoval(node);
// 最后返回被删除的节点
return node;
}
}
// 没有删除,返回null
return null;
}

查找方法:

final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
// 找到对应桶,并且桶不为空
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
// 查看桶中第一个节点
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null) {
if (first instanceof TreeNode)
// 桶中是红黑树,调用TreeNode的getTreeNode方法获取节点,保存到node
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
// 遍历链表
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
// 没找到返回null
return null;
}

9.5.4 工具方法

// 获取key的hash值
// 桶下标 (table.length - 1) & hash(key) 也就是取模运算
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

// 如果x的类是“class C implements Comparable<C>”这种形式,返回x的Class,否则返回null。
static Class<?> comparableClassFor(Object x) {
if (x instanceof Comparable) {
Class<?> c; Type[] ts, as; Type t; ParameterizedType p;
if ((c = x.getClass()) == String.class) // bypass checks
return c;
if ((ts = c.getGenericInterfaces()) != null) {
for (int i = 0; i < ts.length; ++i) {
if (((t = ts[i]) instanceof ParameterizedType) &&
((p = (ParameterizedType)t).getRawType() ==
Comparable.class) &&
(as = p.getActualTypeArguments()) != null &&
as.length == 1 && as[0] == c) // type arg is c
return c;
}
}
}
return null;
}

// 如果x匹配kc (k的筛选可比类),则返回k.compareTo(x),否则返回0。
static int compareComparables(Class<?> kc, Object k, Object x) {
return (x == null || x.getClass() != kc ? 0 :
((Comparable)k).compareTo(x));
}


// 返回大于等于且最接近c的2的幂次数,最大为MAXIMUM_CAPACITY。
// 上面的处理是对传进来的参数进行位操作处理,来实现return出去的数据是2的n次方。举个例子:
// 传进的值是11,减一后变成10;10的二进制表示是1010,进过位操作后,变成1111;1111+1 变成10000 转成10进制是16;是2的4次方。
// 一般来说通过这个方法实际赋的值都是大于等于传进来,期望的值的。

static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}

// 初始化table或扩容表的大小为双倍。如果table为空,则根据在阈值内的初始容量分配。
// 否则,因为我们使用的是2的幂展开,所以每个桶中的元素必须保持相同的索引,或者在新表中以2的幂偏移移动。
// 换句话说,就是元素在原位置,或者在(原位置+原表大小)位置
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
// 说明table中已经有数据了,进行扩容
if (oldCap > 0) {
// 超过最大容量,无法扩容
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
// 容量翻2倍,小于最大容量 并且容量大于默认, 则将threshold翻2倍
// 这里是 1********& 下面会提到
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
// 说明table中没有数据,而threshold已经初始化,则用threshold初始化容量
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
// table没有数据,threshold也没有初始化,则初始化容量和threshold
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
// 更新threshold的值
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
// 创建新table
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
// 如果是 1********&的情况,table中有数据,要将数据搬到新table中
if (oldTab != null) {
// 遍历所有桶
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
// 遍历每个桶内的元素 e为桶里第一个节点
oldTab[j] = null;
if (e.next == null)
// 桶内只有一个元素,放到新table里面
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
// 红黑树,调用TreeNode的split方法
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
// 链表的情况
// 使用loHead和loTail保存链表(原索引)
Node<K,V> loHead = null, loTail = null;
// 使用hiHead和hiTail保存链表(原索引+oldCap索引)
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
// 遍历链表
next = e.next;
// 当前节点e应该放到原索引
if ((e.hash & oldCap) == 0) {
// 第一次将loHead指向第一个节点
if (loTail == null)
loHead = e;
else
// 后面用loTail添加节点到末尾
loTail.next = e;
loTail = e;
}
// 前节点e应该放到原索引+oldCap
else {
// 处理方法同上
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
// 最后将两个链表添加到对应桶中
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}

final void treeifyBin(Node<K,V>[] tab, int hash) {
int n, index; Node<K,V> e;
// 是在转变成树之前,还会有一次判断,只有键值对数量大于64才会发生转换。
// 这是为了避免在哈希表建立初期,多个键值对恰好被放入了同一个链表中而导致不必要的转化。
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
resize();
else if ((e = tab[index = (n - 1) & hash]) != null) {
// e为桶中第一个节点
TreeNode<K,V> hd = null, tl = null;
do {
// 将Node转化为TreeNode
TreeNode<K,V> p = replacementTreeNode(e, null);
if (tl == null)
// 第一个节点
hd = p;
else {
p.prev = tl;
tl.next = p;
}
// tl为上一个节点
tl = p;
} while ((e = e.next) != null);
if ((tab[index] = hd) != null)
// 调用TreeNode的treeify方法,转化为红黑树
hd.treeify(tab);
}
}