1.6版本新增加,队列新增Deque接口和naviagble集合类。
Deque
双端队列,两端队头队尾都可以操作,具有队列和栈性质的数据类型。双端队列中的元素可以从两端弹出,插入和删除。操作提供两种形式,一种是只要操作失败就抛出异常,另一种是操作是否会返回特定值。Deque继承Queue接口,所以也支持FIFO模式操作,同时也支持LIFO基于栈的模式。
ArrayDeque
// 也是基于数组结构存储元素
private transient E[] elements;
// 队头队尾下标
private transient int head;
private transient int tail;
public ArrayDeque() {
elements = (E[]) new Object[16]; // 默认创建容量为16的数组
}
public ArrayDeque(int numElements) {
allocateElements(numElements); // 也可以指定数组容量
}
private void allocateElements(int numElements) {
int initialCapacity = MIN_INITIAL_CAPACITY; // 默认大小8
// Find the best power of two to hold elements.
// Tests "<=" because arrays aren't kept full.
if (numElements >= initialCapacity) { // 容量大小必须是2的整数倍
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
}
elements = (E[]) new Object[initialCapacity];
}
// 添加到队列头
public void addFirst(E e) {
if (e == null) // 元素不允许为null,因为ArrayDeque是根据null来判断// 对应下标位置是否被有元素存在
throw new NullPointerException();
elements[head = (head - 1) & (elements.length - 1)] = e;
if (head == tail)
doubleCapacity(); // 扩容
}
// 每次双倍扩容
private void doubleCapacity() {
assert head == tail; // 数组满了才扩容
int p = head;
int n = elements.length;
int r = n - p; // number of elements to the right of p
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 = (E[])a;
head = 0;
tail = n;
}
// 删除指定位置的元素
private boolean delete(int i) {
checkInvariants();
final E[] elements = this.elements;
final int mask = elements.length - 1;
final int h = head;
final int t = tail;
final int front = (i - h) & mask;
final int back = (t - i) & mask;
// Invariant: head <= i < tail mod circularity
if (front >= ((t - h) & mask))
throw new ConcurrentModificationException();
// Optimize for least element motion
if (front < back) {
if (h <= i) {
System.arraycopy(elements, h, elements, h + 1, front);
} else { // Wrap around
System.arraycopy(elements, 0, elements, 1, i);
elements[0] = elements[mask];
System.arraycopy(elements, h, elements, h + 1, mask - h);
}
elements[h] = null;
head = (h + 1) & mask;
return false;
} else {
if (i < t) { // Copy the null tail as well
System.arraycopy(elements, i + 1, elements, i, back);
tail = t - 1;
} else { // Wrap around
System.arraycopy(elements, i + 1, elements, i, mask - i);
elements[mask] = elements[0];
System.arraycopy(elements, 1, elements, 0, t);
tail = (t - 1) & mask;
}
return true;
}
}
BlockingDeque
Deque添加阻塞操作,主要的方法有:
- offerFirst(E e) 将元素添加到队列头
- offerLast(E e) 将元素添加到队列尾
- offerFirst(E e, long timeout, TimeUnit unit) 将元素添加到队列头,如果在指定时间内未添加成功则抛出异常
- offerFirst(E e, long timeout, TimeUnit unit) 将元素添加到队列尾,如果在指定时间内未添加成功则抛出异常
- takeFirst() 队头元素出队
- takeLast() 队尾元素出队
- pollFirst(long timeout, TimeUnit unit) 队头元素出队,如果队列为空则一直等待直到超过指定时间
- pollLast(long timeout, TimeUnit unit) 队尾元素出队,如果队列为空则一直等待直到超过指定时间
LinkedBlockingDeque
基于双向链表实现,可指定容量大小。
等待指定时间,都是通过Condition.awaitNanos方法来实现等待。
ConcurrentSkipListMap
Java Compiler API
Java编程语言编译器可以用javac命令读取以Java编程语言编写的源文件,并将它们编译为字节码class文件。编译器也可以使用注解找到源文件和类文件并使用comiler API进行编译。编译器是一个命令行工具,但也可以使用Java compiler API调用。
我们可以用JDK6的Compiler API(JSR 199)去动态编译Java源文件,Compiler API结合反射功能就可以实现动态的产生Java代码并编译执行这些代码,有点动态语言的特征。这个特性对于某些需要用到动态编译的应用程序相当有用,比如JSP Web Server,当我们手动修改JSP后,是不希望需要重启Web Server才可以看到效果的,这时候我们就可以用Compiler API来实现动态编译JSP文件,当然,现在的JSP Web Server也是支持JSP热部署的,现在的JSP Web Server通过在运行期间通过Runtime.exec或ProcessBuilder来调用javac来编译代码,这种方式需要我们产生另一个进程去做编译工作,不够优雅而且容易使代码依赖与特定的操作系统;Compiler API通过一套易用的标准的API提供了更加丰富的方式去做动态编译,而且是跨平台的。