JDK容器与并发—Queue—PriorityQueue

概述

      基于优先堆的无界优先队列,非线程安全。

1)add、offer、remove、poll的时间复杂度为O(log(n));remove(Object)、contains(Object)为线性时间;peek、element、size为固定时间;

2)迭代器fail-fast。

数据结构

      基于数组的平衡二叉堆:

// queue[n]元素的两个children为queue[2*n+1]、queue[2*(n+1)]
// queue[n]<=queue[2*n+1]且queue[n]<=queue[2*(n+1)]
// queue[0]为最小值
private transient Object[] queue;

构造器

// 无参构造,初始容量为DEFAULT_INITIAL_CAPACITY=11,采用元素自然排序
public PriorityQueue() {
	this(DEFAULT_INITIAL_CAPACITY, null);
}

// 带初始容量参数构造
public PriorityQueue(int initialCapacity) {
	this(initialCapacity, null);
}

// 带初始容量、comparator参数构造
public PriorityQueue(int initialCapacity,
					 Comparator 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;
}

// 带Collection参数构造
public PriorityQueue(Collection c) {
	if (c instanceof SortedSet) { // SortedSets是特殊的平衡二叉堆,但需要稍调整,转化为Object[],元素不能为null
		SortedSet ss = (SortedSet) c;
		this.comparator = (Comparator) ss.comparator();
		initElementsFromCollection(ss);
	}
	else if (c instanceof PriorityQueue) { // PriorityQueue参数,直接拿来用
		PriorityQueue pq = (PriorityQueue) c;
		this.comparator = (Comparator) pq.comparator();
		initFromPriorityQueue(pq);
	}
	else {									  // 对普通的Collection,转化为Object[],元素不能为null,还要对其heapify
		this.comparator = null;
		initFromCollection(c);
	}
}

// 带PriorityQueue参数构造
public PriorityQueue(PriorityQueue c) {
	this.comparator = (Comparator) c.comparator();
	initFromPriorityQueue(c);
}

// 带SortedSet参数构造
public PriorityQueue(SortedSet c) {
	this.comparator = (Comparator) c.comparator();
	initElementsFromCollection(c);
}

增删查

容器调整策略(避免无限制扩展)

      步骤:
1)当queue已满,若有元素入队请求,则进行容量扩展;
2)oldCapacity小于64则容量翻倍;否则增长50%;
3)检查newCapacity是否在MAX_ARRAY_SIZE范围内,若minCapacity有overflow,抛出OutOfMemoryError异常;否则容量最大不超过Integer.MAX_VALUE;
4)创建新容量的Object[],将旧的queue元素复制过来。

// 当queue已满时,则进行容量扩展
if (i >= queue.length)
		grow(i + 1);
		
private void grow(int minCapacity) {
	int oldCapacity = queue.length;
	// oldCapacity小于64则容量翻倍;否则增长50%
	int newCapacity = oldCapacity + ((oldCapacity < 64) ?
									 (oldCapacity + 2) :
									 (oldCapacity >> 1));
	// newCapacity都超过了MAX_ARRAY_SIZE,检查minCapacity是否有溢出
	if (newCapacity - MAX_ARRAY_SIZE > 0)
		newCapacity = hugeCapacity(minCapacity);
	queue = Arrays.copyOf(queue, newCapacity);
}

 private static int hugeCapacity(int minCapacity) {
	if (minCapacity < 0) // overflow,抛出OutOfMemoryError异常
		throw new OutOfMemoryError();
	return (minCapacity > MAX_ARRAY_SIZE) ? // 最大不能超过Integer.MAX_VALUE
		Integer.MAX_VALUE :
		MAX_ARRAY_SIZE;
}

基础方法

// 对元素x,从k往前移,保持二叉堆的平衡性
private void siftUpUsingComparator(int k, E x) {
	while (k > 0) {
		int parent = (k - 1) >>> 1; 
		Object e = queue[parent];
		if (comparator.compare(x, (E) e) >= 0)
			break;
		queue[k] = e;
		k = parent;
	}
	queue[k] = x;
}

// 对元素x,从k往后移,保持二叉堆的平衡性
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 &&
			comparator.compare((E) c, (E) queue[right]) > 0)
			c = queue[child = right];
		if (comparator.compare(x, (E) c) <= 0)
			break;
		queue[k] = c;
		k = child;
	}
	queue[k] = x;
}

      步骤:
1)检查队列是否已满,若满则进行容量扩展;
2)从队尾,对元素进行siftUp,保持二叉堆的平衡性;
3)返回true

// 因无容量限制,add委托给offer
public boolean add(E e) {
	return offer(e);
}

// 将元素入队
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);   // 从对尾,对元素进行siftUp
	return true;
}

步骤:
1)检查队列是否为空,为空则返回null;
2)取出队列最后一个元素,从索引0开始,对其进行siftDown,保持二叉堆的平衡性;
3)返回队首元素值。

// 将队首元素出队
public E poll() {
	if (size == 0)			// 队列为空,返回null
		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);	   // 从0开始,对最后一个元素进行siftDown
	return result;
}


// Collection方法,删除内部元素
public boolean remove(Object o) {
	int i = indexOf(o);		// 确定索引位置
	if (i == -1)
		return false;
	else {
		removeAt(i);	   // 删除该元素
		return true;
	}
}

// 删除索引i的元素
private E removeAt(int i) {
	assert i >= 0 && i < size;
	modCount++;
	int s = --size;
	if (s == i) 				   // 若删除的索引i元素为最后一个元素,直接赋为null
		queue[i] = null;
	else {
		E moved = (E) queue[s];	   // 取出队列最后一个元素
		queue[s] = null;
		siftDown(i, moved);		   // 从索引i开始,对最后一个元素进行siftDown
		if (queue[i] == moved) {   // 若取出的最后一个元素比i+1及其后的元素都小
			siftUp(i, moved);	   // 对取出的最后一个元素,从索引i开始,进行siftUp
			if (queue[i] != moved)
				return moved;
		}
	}
	return null;
}

步骤:
1)检查队列是否为空,为空则返回null;
2)返回队首元素值。

public E peek() {
	if (size == 0)
		return null;
	return (E) queue[0];
}

迭代器

      不保证元素的迭代顺序:

private final class Itr implements Iterator {

	private int cursor = 0;

	private int lastRet = -1;

	// 某些元素因为删除,为了保持二叉堆的平衡性,更换到遍历过的索引位置
	// 为了确保都遍历到,这里缓存下
	private ArrayDeque forgetMeNot = null;

	private E lastRetElt = null;

	private int expectedModCount = modCount;

	public boolean hasNext() {
		return cursor < size ||
			(forgetMeNot != null && !forgetMeNot.isEmpty());
	}

	public E next() {
		if (expectedModCount != modCount)
			throw new ConcurrentModificationException();
		if (cursor < size)
			return (E) queue[lastRet = cursor++];
		if (forgetMeNot != null) {
			lastRet = -1;
			lastRetElt = forgetMeNot.poll();
			if (lastRetElt != null)
				return lastRetElt;
		}
		throw new NoSuchElementException();
	}

	public void remove() {
		if (expectedModCount != modCount)
			throw new ConcurrentModificationException();
		if (lastRet != -1) {
			E moved = PriorityQueue.this.removeAt(lastRet);
			lastRet = -1;
			if (moved == null)
				cursor--;
			else { // moved不为null,说明最后一个元素更换到了遍历前的位置
				if (forgetMeNot == null)
					forgetMeNot = new ArrayDeque<>();
				forgetMeNot.add(moved);
			}
		} else if (lastRetElt != null) {
			PriorityQueue.this.removeEq(lastRetElt);
			lastRetElt = null;
		} else {
			throw new IllegalStateException();
		}
		expectedModCount = modCount;
	}
}

特性

      用平衡二叉堆的性质保证优先。

你可能感兴趣的:(JDK容器与并发)