JDK7集合框架原始碼學習-ArrayDeque

壹頁書發表於2016-10-13
參考:

http://brokendreams.iteye.com/blog/1918475

先看allocateElements函式

  1. /** 
  2.  * Allocate empty array to hold the given number of elements. 
  3.  * 
  4.  * @param numElements  the number of elements to hold 
  5.  */  
  6. private void allocateElements(int numElements) {  
  7.     int initialCapacity = MIN_INITIAL_CAPACITY;  
  8.     // Find the best power of two to hold elements.  
  9.     // Tests "<=" because arrays aren't kept full.  
  10.     if (numElements >= initialCapacity) {  
  11.         initialCapacity = numElements;  
  12.         initialCapacity |= (initialCapacity >>>  1);  
  13.         initialCapacity |= (initialCapacity >>>  2);  
  14.         initialCapacity |= (initialCapacity >>>  4);  
  15.         initialCapacity |= (initialCapacity >>>  8);  
  16.         initialCapacity |= (initialCapacity >>> 16);  
  17.         initialCapacity++;  
  18.   
  19.         if (initialCapacity < 0)   // Too many elements, must back off  
  20.             initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements  
  21.     }  
  22.     elements = (E[]) new Object[initialCapacity];  


他主要是給定一個值numElements,如果大於預設值,則找一個大於numElements的最小的2的平方值.
他擴充套件的序列是 8,16,32,64,128...
如果numElements為17,則擴充套件為32.

轉載:
前面幾句很好理解,如果numElements太小,那麼佇列長度就取預設初始化長度;
後面一坨程式碼的意思:如果numElements大於預設初始化長度,就取最小的n使得:n = 2^k(k為整數), 且n > numElements。
求n的方法有很多:從小到大列舉k、二分k等等;可能作者覺得這些方法都太low了,又熟練掌握二進位制運算技巧,於是才有了這幾行程式碼。
下面簡單分析一下程式碼的運算過程,注:">>>"代表按位右移:
假設a=1xxxxxxxxxxxx...(base 2, x代表該位任意為0或1)
首先a |= (a >>> 1)之後,a => 11xxxxxxxx...(最高兩位是1)
然後a |= (a>>> 2): a => 1111xxxxxxxxx...(最高四位是1)
再a |= (a>>> 4): a => 11111111xxxxxxx...(最高八位是1)
........
最終,a的所有低位也都變成了1,即11111111...111(全是1)
再a++ 就變成了10000000000...000(加一之後進位,比原來的二進位制串多了一位,且第一位是1,其它位都是0),這個演算法不僅時間效率高,而且只用到了一個變數,真可謂是短小精悍,在HashMap裡也可以看到這個演算法的身影。



  1.     public static void main(String[] args) {
  2.          int initialCapacity = 17;
  3.         
  4.          System.out.println(Integer.toBinaryString(initialCapacity)+"(initialCapacity)");
  5.          System.out.println(Integer.toBinaryString(initialCapacity>>>1)+"(移位)");
  6.          initialCapacity |= (initialCapacity >>> 1);

  7.          System.out.println(Integer.toBinaryString(initialCapacity)+"(initialCapacity)");
  8.          System.out.println(Integer.toBinaryString(initialCapacity>>>2)+"(移位)");
  9.          initialCapacity |= (initialCapacity >>> 2);

  10.          System.out.println(Integer.toBinaryString(initialCapacity)+"(initialCapacity)");
  11.          System.out.println(Integer.toBinaryString(initialCapacity>>>4)+"(移位)");
  12.          initialCapacity |= (initialCapacity >>> 4);

  13.          System.out.println(Integer.toBinaryString(initialCapacity)+"(initialCapacity)");
  14.          System.out.println(Integer.toBinaryString(initialCapacity>>>8)+"(移位)");
  15.          initialCapacity |= (initialCapacity >>> 8);

  16.          System.out.println(Integer.toBinaryString(initialCapacity)+"(initialCapacity)");
  17.          System.out.println(Integer.toBinaryString(initialCapacity>>>16)+"(移位)");
  18.          initialCapacity |= (initialCapacity >>> 16);
  19.          
  20.          initialCapacity++;
  21.          System.out.println(initialCapacity);
  22.     }

結果:
10001(initialCapacity)
1000(移位)
11001(initialCapacity)
110(移位)
11111(initialCapacity)
1(移位)
11111(initialCapacity)
0(移位)
11111(initialCapacity)
0(移位)
32


也就是說,以17為例(二進位制10001),他會將二進位制數字補全為  11111 最後再加一.
也就是 100000,轉為十進位制數字就是32.


再看addFirst函式和addLast函式

  1.     public void addFirst(E e) {
  2.         if (e == null)
  3.             throw new NullPointerException();
  4.         elements[head = (head - 1) & (elements.length - 1)] = e;
  5.         if (head == tail)
  6.             doubleCapacity();
  7.     }

  8.     public void addLast(E e) {
  9.         if (e == null)
  10.             throw new NullPointerException();
  11.         elements[tail] = e;
  12.         if ( (tail = (tail + 1) & (elements.length - 1)) == head)
  13.             doubleCapacity();
  14.     }

初始化的時候,head和tail都是0
(elements.length - 1)在未擴充套件的時候,應該是一個固定值.(7,15,31,63...)

一旦容量不夠,則擴充套件一倍.

  1.     private void doubleCapacity() {
  2.         assert head == tail;
  3.         int p = head;
  4.         int n = elements.length;
  5.         int r = n - p; // number of elements to the right of p
  6.         int newCapacity = n << 1;
  7.         if (newCapacity < 0)
  8.             throw new IllegalStateException("Sorry, deque too big");
  9.         Object[] a = new Object[newCapacity];
  10.         System.arraycopy(elements, p, a, 0, r);
  11.         System.arraycopy(elements, 0, a, r, p);
  12.         elements = (E[])a;
  13.         head = 0;
  14.         tail = n;
  15.     }

以預設的8個位置為例,雙倍擴充套件之前的資料假設為
5,6,7,8,4,3,2,1
addFirst 1-4
addLast  4-8

這時候擴充套件為16個位置,陣列位置為
4,3,2,1,5,6,7,8,null,null....





來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29254281/viewspace-2126289/,如需轉載,請註明出處,否則將追究法律責任。

相關文章