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

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

下面两行是什么意思? 是位运算吗?他们为什么使用它以及这里的目的是什么?

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

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

最佳答案

看一下初始化代码——双端队列表示为一个数组,其大小始终是 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(截断前导零)。

因此,当 pollFirst 方法中的 head 元素被重置时(提前一个),使用位运算符 & 来使双端队列循环。同样,如果 elements 的大小为 16,而当前 head 的大小为 15,则 head + 1 将为 16,因此:

10000
01111
-----
00000

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

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

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

意思是 tail 减一,但从 0 移动到 elements.length - 1

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

关于java - 为什么 ArrayDeque 类在 pollFirst 方法中使用按位运算?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36825213/

相关文章:

java - 从与类路径不同的位置加载 spring XML

javascript - 试图理解 [object HTMLCollection] From JavaScript .innerHTML Right Function?

java - 对混合数据列表进行排序?

algorithm - 我认为 "NlogN"是 "N"乘以 "logN",但为什么它被描述为 "double PLUS an amount proportional to N"

c++ - 按位操作函数

javascript - 为什么与1(&1)按位运算总是返回0或1

java - 方法解析和调用如何在 Python 内部工作?

java - 从 Spring security 3 升级到 Spring security 4 后出现异常?

java - Android - onOptionsItemSelected 项目 ID 不匹配

c - 转移 __m128i 的最佳方法?