为什么ArrayDeque类在pollFirst方法中使用按位运算?

时间:2016-04-24 15:32:33

标签: java collections bitwise-operators bitwise-and arraydeque

我通过java源代码看看尝试学习集合的实现。 在ArrayDeque类中发现了一件有趣的事情。

public E pollFirst() {
    int h = head;
    @SuppressWarnings("unchecked")
    E result = (E) elements[h];
    // Element is null if deque empty
    if (result == null)
        return null;
    elements[h] = null;     // Must null out slot
    head = (h + 1) & (elements.length - 1);
    return result;
}

public E pollLast() {
    int t = (tail - 1) & (elements.length - 1);
    @SuppressWarnings("unchecked")
    E result = (E) elements[t];
    if (result == null)
        return null;
    elements[t] = null;
    tail = t;
    return result;
}

以下2行是什么意思? 这是一个按位操作吗?为什么他们使用它以及这里的目的是什么?

    head = (h + 1) & (elements.length - 1);
    int t = (tail - 1) & (elements.length - 1);

我知道使用按位的一个方案是在1个变量中打包2个值。但似乎事实并非如此。

2 个答案:

答案 0 :(得分:6)

看看初始化代码 - Deque表示为一个大小始终为2的幂的数组:

195    public ArrayDeque(int numElements) {
196        allocateElements(numElements);
197    }

124    private void allocateElements(int numElements) {
125        int initialCapacity = MIN_INITIAL_CAPACITY;
126        // Find the best power of two to hold elements.
127        // Tests "<=" because arrays aren't kept full.
128        if (numElements >= initialCapacity) {
129            initialCapacity = numElements;
130            initialCapacity |= (initialCapacity >>>  1);
131            initialCapacity |= (initialCapacity >>>  2);
132            initialCapacity |= (initialCapacity >>>  4);
133            initialCapacity |= (initialCapacity >>>  8);
134            initialCapacity |= (initialCapacity >>> 16);
135            initialCapacity++;
136
137            if (initialCapacity < 0)   // Too many elements, must back off
138                initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements
139        }
140        elements = (E[]) new Object[initialCapacity];
141    }

所以elements.length - 1是二进制基本上是在超出数组大小之前的一系列1位。

例如,如果elements初始化为大小为16的数组,则elements.length - 1为15,表示0..001111(截断的前导零)。

因此,当head方法中重置pollFirst元素(由1提前)时,将使用按位&运算符以使Deque循环。同样,如果elements的大小为16且当前head为15,那么head + 1将为16,因此:

10000
01111
-----
00000

意思是,head被重置为索引0.这允许您重用已经分配的空间,使用数组及其插入和检索的O(1)效率,而无需分配新的空间。

对于重置pollLast变量的tail也是如此,即如果tail为0且elements的大小为16,则:

tail      00000
tail-1    11111   (-1 in two's complement)
          01111
          -----
          01111

含义tail递减一个值,但从0移到elements.length - 1

你可以用更复杂的&#34;来实现同样的目标。 if语句(或使用三元运算符),但这是实现循环数组的一种相当常见且可接受的方式。

答案 1 :(得分:0)

这是一种更有效的计算(head+1) % elements.length的方法,我们可以这样做,因为elements.length是2的幂。它更有效,因为 mod 运算符比较昂贵到按位和

另一方面,仅使用tail mod 不起作用,因为在Java中-1 % N == -1