7

我通过看java源代码尝试学习collection的实现。在 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);

我知道使用按位的一种情况是将 2 个值打包到 1 个变量中。但似乎情况并非如此。

4

2 回答 2

9

看一下初始化代码 - 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(提前一),使用按位运算&符来使 Deque 循环。同样,如果elements大小为 16,当前head为 15,则为head + 116,因此:

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

您可以使用更“复杂”的 if 语句(或使用三元运算符)实现相同的目的,但是这是实现循环数组的一种相当常见且可接受的方式。

于 2016-04-24T15:48:17.427 回答
0

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

另一方面,只使用modtail行不通的,因为在 Java 中,-1 % N == -1.

于 2016-05-22T00:08:10.923 回答