1. 程式人生 > >對Android中的堆疊的理解(Stack)

對Android中的堆疊的理解(Stack)

         棧(stack)在電腦科學中是限定僅在表尾進行插入或刪除操作的線性表。棧是一種資料結構,它按照後進先出的原則儲存資料,先進入的資料被壓入棧底,最後的資料在棧頂,需要讀資料的時候從棧頂開始彈出資料。棧是隻能在某一端插入和刪除的特殊線性表。用桶堆積物品,先堆進來的壓在底下,隨後一件一件往上堆。取走時,只能從上面一件一件取。讀和取都在頂部進行,底部一般是不動的。棧就是一種類似桶堆積物品的資料結構,進行刪除和插入的一端稱棧頂,另一端稱棧底。插入一般稱為進棧,刪除則稱為退棧。 棧也稱為後進先出表。

      Heap

    堆(heap)又被為優先佇列(priority queue)。儘管名為優先佇列,但堆並不是佇列。回憶一下,在佇列中,我們可以進行的限定操作是dequeue和enqueue。dequeue是按照進入佇列的先後順序來取出元素。而在堆中,我們不是按照元素進入佇列的先後順序取出元素的,而是按照元素的優先順序取出元素。

這就好像候機的時候,無論誰先到達候機廳,總是頭等艙的乘客先登機,然後是商務艙的乘客,最後是經濟艙的乘客。每個乘客都有頭等艙、商務艙、經濟艙三種個鍵值(key)中的一個。頭等艙->商務艙->經濟艙依次享有從高到低的優先順序。




public class Stack<E>
extends Vector<E>

Stack 類表示後進先出(LIFO)的物件堆疊。它通過五個操作對類 Vector 進行了擴充套件 ,允許將向量視為堆疊。它提供了通常的pushpop 操作,以及取堆疊頂點的 peek 方法、測試堆疊是否為空的 empty 方法、在堆疊中查詢項並確定到堆疊頂距離的search 方法。

首次建立堆疊時,它不包含項。

Deque 介面及其實現提供了 LIFO 堆疊操作的更完整和更一致的 set,應該優先使用此 set,而非此類。例如:

   Deque<Integer> stack = new ArrayDeque<Integer>();


       如果你的應用中涉及到的東西比較耗記憶體的話,比如:相機、第三方地圖、騰訊、新浪、錄音、視訊播放、大量圖片時,如果這些東西同時存在於應用中時,會有很多奇怪的問題出現,自動退出還不報錯等等一系列的問題,還有,如果我們的應用中使用startActivity()過多而且並沒有及時finish()掉的話,也會出現這樣那樣的問題,比如:退出應用時沒有退出乾淨,或者莫名其妙的報OOM,啟動的服務自動掛起什麼的!       其實,Google已經提供了一套完整的機制讓開發人員控制活動棧與任務棧
像這樣的跳轉我們在開發的過程中算是很長見到的了,在這裡我就不貼程式碼了 ,假如就是有三個活動視窗(Activity1,Activity2
Activity3)按先後順序 從Activity1--startActivity()Activity2再到Activity3這個過程大家應該可以想象的到,在這個過程生成的活動堆疊如圖所示:

http://img1.51cto.com/attachment/201206/160525368.jpg

    這個地方說明下,有時候大家可以想著從1到2時可以繫結資料完成回顯,但是如果要簡單的回顯用繫結或startActivityForResult()這兩種方式啟動,但是如果涉及到三個以上的活動惑更多活動之間的跳轉時,有時候不得不必須重新啟動新的活動,也就出現了前面的1>>2>>3>>4>>>>>>>甚至更多的活動跳轉,這樣一個個關閉有時候還是關不乾淨,應用退出的時候也還是不乾淨的,更搞笑的是有時候還有使用者在多個活動之間跳轉並不進行任何資料操作時還要求返回上一個Activity時你就不能直接finish掉上一個Activity,不然人家說你跳轉不對,針對這個問題我們來看下Google提供的堆疊任務控制機制吧,很簡單,用Flag來控制,這個時候就有個問題,提供的方法有setFlag()、addFlag(),這兩個肯定有什麼區別的,不然不會出現兩個控制Flag的方法的

如果是點選回退鍵的過程中也會有不一樣同樣點選了六次按鈕之後按的返回鍵,第一種效果必須點選六次Back鍵後方可退出,而第二種效果只點擊一次即可退出,這就是Flag的魅力,激動….再來看Flag都有哪幾種吧,此處我列在這個地方,上面兩個效果中設定的是:i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);另外還有很多可以控制活動堆疊與任務棧的Flag,小馬在這個地方隨便列出兩個,剩餘的Flag值以截圖的形式顯示,節約時間:

  1. i.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
  2. i.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)其它:

Activity和Task(棧)的關係

 Task就像一個容器,而Activity就相當與填充這個容器的東西,第一個東西(Activity)則會處於最下面,最後新增的東西(Activity)則會在最低端。從Task中取出東西(Activity)則是從最頂端取出。

  二、介面跳轉和服務的啟動都會用到Intent,現在介紹Intent Flag是關於Activity的跳轉
  Intent intent = new Intent(this,xxx.class);
  //如果activity在task存在,拿到最頂端,不會啟動新的Activity
  intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
  //如果activity在task存在,將Activity之上的所有Activity結束掉
  intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
  //預設的跳轉型別,將Activity放到一個新的Task中
  intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  //如果Activity已經執行到了Task,再次跳轉不會在執行這個Activity
  intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

  大家可以很清楚的看到以後所含的標誌中有針對於TASK的,對吧?指的就是任務堆疊,至於什麼是任務堆疊,大家不用太過糾結於與活動堆疊之間的概念什麼的,只記住一點:如果你在應用中啟動Activity的時候沒加任務堆疊的控制Flag時,開發環境肯定報錯,而且提示的很準確 ,就是:你缺少新增任務堆疊Flag標誌位,具體少了哪個標誌,開發環境也會很準確的指出,必須要你新增才可正常編譯通過!下面列下小馬犯的錯誤,就是在一個Activity找到一個amr錄音檔案,直接以下面的方式啟動去播放錄音,猛報錯:

  1. Intent i = new Intent(Intent.ACTION_VIEW); 
  2.       i.putExtra("filePath",path); 
  3.      startActivity(i); 

       如果加了 i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);操作下任務堆疊就可以了,具體的原因,也可以用一句話來總結:如果在一個Activity中同一時間,要操作不用的功能,比如:跳轉時還要操作視訊錄音檔案的播放什麼的,都得設定新的任務棧來啟動開啟,如果不啟動新任務棧的話,有時候會無原無故的顯示空白還不報錯!上面的錯只是一個小點,小到可以忽略不講,寫在這是提醒大家,該加的時候必須加Flag,至於什麼時候加,大家可以參照下官方的文件及下面小馬貼出的官方文件中解釋堆疊的圖解,加以理解學習,如下所示:Figure2:不多解釋,就是在A B 丙個任務堆疊,如果使用者正在於B任務堆疊進行互動時,A在等待喚醒,反之則反

          Figure3: 下面這個就好玩了,學習了下官方的文件,講的是:無論啟動了一個新的任務堆疊或者在同一堆疊中來啟動一個活動,按返回鍵也還是會返回到使用者之前操作的Activity,如果以單例堆疊(類似單位模式)載入的話,就會在後臺生成一個針對於此活動的單獨的一個任務堆疊,當這個任務堆疊被喚醒到前臺時,此時的返回堆疊中就包含了從前幾個任務傳遞過來的棧頂的所有Activity,棧頂與棧底的顯示關係如果下圖:

            這個地方順帶著講下,在控制活動堆疊時方式只有一種,就是直接在.java檔案中setFlag,如果是控制任務堆疊的話可以以addFlag或直接在全域性配置檔案中新增配置的方式來控制,大家可以直接在AndroidManifest.xml檔案中activity節點中新增哪下屬性:taskAffinity、launchMode、allowTaskReparenting、clearTaskOnLaunch、alwaysRetainTaskState、finishOnTaskLaunch,兩種控制任務堆疊的方式換湯不換藥,大家看個人習慣選擇使用就可以了…切記,用的時候一定搞清楚你要加的標誌位是什麼意思,不要看到個task就addFlag,設定Flag是為了讓應用更乾淨,控制更嚴密,如果加錯了標誌位,應用是不會報任何錯的,只是出現怪異的跳轉與關閉!!!

Stack是一個後進先出(last in first out,LIFO)的堆疊,在Vector類的基礎上擴充套件5個方法而來

Deque(雙端佇列)比起Stack具有更好的完整性和一致性,應該被優先使用

  1. E push(E item)   
  2.          把項壓入堆疊頂部。   
  3. E pop()   
  4.          移除堆疊頂部的物件,並作為此函式的值返回該物件。   
  5. E peek()   
  6.          檢視堆疊頂部的物件,但不從堆疊中移除它。   
  7. boolean empty()   
  8.          測試堆疊是否為空。    
  9. int search(Object o)   
  10.          返回物件在堆疊中的位置,以 1 為基數。  

Stack本身通過擴充套件Vector而來,而Vector本身是一個可增長的物件陣列( a growable array of objects)那麼這個陣列的哪裡作為Stack的棧頂,哪裡作為Stack的棧底?

答案只能從原始碼中尋找,jdk1.6:

  1. publicclass Stack<E> extends Vector<E> {  
  2.     /** 
  3.      * Creates an empty Stack. 
  4.      */
  5.     public Stack() {  
  6.     }  
  7.     /** 
  8.      * Pushes an item onto the top of this stack. This has exactly 
  9.      * the same effect as: 
  10.      * <blockquote><pre> 
  11.      * addElement(item)</pre></blockquote> 
  12.      * 
  13.      * @param   item   the item to be pushed onto this stack. 
  14.      * @return  the <code>item</code> argument. 
  15.      * @see     java.util.Vector#addElement 
  16.      */
  17.     public E push(E item) {  
  18.     addElement(item);  
  19.     return item;  
  20.     }  
  21.     /** 
  22.      * Removes the object at the top of this stack and returns that 
  23.      * object as the value of this function. 
  24.      * 
  25.      * @return     The object at the top of this stack (the last item 
  26.      *             of the <tt>Vector</tt> object). 
  27.      * @exception  EmptyStackException  if this stack is empty. 
  28.      */
  29.     publicsynchronized E pop() {  
  30.     E   obj;  
  31.     int len = size();  
  32.     obj = peek();  
  33.     removeElementAt(len - 1);  
  34.     return obj;  
  35.     }  
  36.     /** 
  37.      * Looks at the object at the top of this stack without removing it 
  38.      * from the stack. 
  39.      * 
  40.      * @return     the object at the top of this stack (the last item 
  41.      *             of the <tt>Vector</tt> object). 
  42.      * @exception  EmptyStackException  if this stack is empty. 
  43.      */
  44.     publicsynchronized E peek() {  
  45.     int len = size();  
  46.     if (len == 0)  
  47.         thrownew EmptyStackException();  
  48.     return elementAt(len - 1);  
  49.     }  
  50.     /** 
  51.      * Tests if this stack is empty. 
  52.      * 
  53.      * @return  <code>true</code> if and only if this stack contains 
  54.      *          no items; <code>false</code> otherwise. 
  55.      */
  56.     publicboolean empty() {  
  57.     return size() == 0;  
  58.     }  
  59.     /** 
  60.      * Returns the 1-based position where an object is on this stack. 
  61.      * If the object <tt>o</tt> occurs as an item in this stack, this 
  62.      * method returns the distance from the top of the stack of the 
  63.      * occurrence nearest the top of the stack; the topmost item on the 
  64.      * stack is considered to be at distance <tt>1</tt>. The <tt>equals</tt> 
  65.      * method is used to compare <tt>o</tt> to the 
  66.      * items in this stack. 
  67.      * 
  68.      * @param   o   the desired object. 
  69.      * @return  the 1-based position from the top of the stack where 
  70.      *          the object is located; the return value <code>-1</code> 
  71.      *          indicates that the object is not on the stack. 
  72.      */
  73.     publicsynchronizedint search(Object o) {  
  74.     int i = lastIndexOf(o);  
  75.     if (i >= 0) {  
  76.         return size() - i;  
  77.     }  
  78.     return -1;  
  79.     }  
  80.     /** use serialVersionUID from JDK 1.0.2 for interoperability */
  81.     privatestaticfinallong serialVersionUID = 1224463164541339165L;  
  82. }  

通過peek()方法註釋The object at the top of this stack (the last item of the Vector object,可以發現陣列(Vector)的最後一位即為Stack的棧頂

pop、peek以及search方法本身進行了同步

push方法呼叫了父類的addElement方法

empty方法呼叫了父類的size方法

Vector類為執行緒安全類

綜上,Stack類為執行緒安全類(多個方法呼叫而產生的資料不一致問題屬於原子性問題的範疇)

  1. publicclass Test {  
  2.     publicstaticvoid main(String[] args) {  
  3.         Stack<String> s = new Stack<String>();  
  4.         System.out.println("------isEmpty");  
  5.         System.out.println(s.isEmpty());  
  6.         System.out.println("------push");  
  7.         s.push("1");  
  8.         s.push("2");  
  9.         s.push("3");  
  10.         Test.it(s);  
  11.         System.out.println("------pop");  
  12.         String str = s.pop();  
  13.         System.out.println(str);  
  14.         Test.it(s);  
  15.         System.out.println("------peek");  
  16.         str = s.peek();  
  17.         System.out.println(str);  
  18.         Test.it(s);  
  19.         System.out.println("------search");  
  20.         int i = s.search("2");  
  21.         System.out.println(i);  
  22.         i = s.search("1");  
  23.         System.out.println(i);  
  24.         i = s.search("none");  
  25.         System.out.println(i);  
  26.     }  
  27.     publicstaticvoid it(Stack<String> s){  
  28.         System.out.print("iterator:");  
  29.         Iterator<String> it = s.iterator();  
  30.         while(it.hasNext()){  
  31.             System.out.print(it.next()+";");  
  32.         }  
  33.         System.out.print("\n");  
  34.     }  
  35. }  

結果:

  1. ------isEmpty
  2. true
  3. ------push
  4. iterator:1;2;3;    
  5. ------pop
  6. 3       --棧頂是陣列最後一個
  7. iterator:1;2;  
  8. ------peek
  9. 2       --pop取後刪掉,peek只取不刪
  10. iterator:1;2;  
  11. ------search    
  12. 1       --以1為基數,即棧頂為1
  13. 2       --和棧頂見的距離為2-1=1
  14. -1      --不存在於棧中

Stack並不要求其中儲存資料的唯一性,當Stack中有多個相同的item時,呼叫search方法,只返回與查詢物件equal並且離棧頂最近的item與棧頂間距離(見原始碼中search方法說明)