您的位置:首页 > 移动开发 > Android开发

对Android中的堆栈的理解(Stack<E>)

2015-12-17 09:39 399 查看
             堆栈空间分配

栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值局部变量的值等。其操作方式类似于数据结构中的栈。
堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。

堆栈缓存方式

栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放。
堆则是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些

Stack

         栈(stack)在计算机科学中是限定仅在表尾进行插入或删除操作的线性表。栈是一种数据结构,它按照后进先出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据。栈是只能在某一端插入和删除的特殊线性表。用桶堆积物品,先堆进来的压在底下,随后一件一件往上堆。取走时,只能从上面一件一件取。读和取都在顶部进行,底部一般是不动的。栈就是一种类似桶堆积物品的数据结构,进行删除和插入的一端称栈顶,另一端称栈底。插入一般称为进栈,删除则称为退栈。
栈也称为后进先出表。
     
Heap

    堆(heap)又被为优先队列(priority queue)。尽管名为优先队列,但堆并不是队列。回忆一下,在队列中,我们可以进行的限定操作是dequeue和enqueue。dequeue是按照进入队列的先后顺序来取出元素。而在堆中,我们不是按照元素进入队列的先后顺序取出元素的,而是按照元素的优先级取出元素。

这就好像候机的时候,无论谁先到达候机厅,总是头等舱的乘客先登机,然后是商务舱的乘客,最后是经济舱的乘客。每个乘客都有头等舱、商务舱、经济舱三种个键值(key)中的一个。头等舱->商务舱->经济舱依次享有从高到低的优先级。

   Android 之活动任务堆栈

java.lang.Object
java.util.AbstractCollection<E>
java.util.AbstractList<E>
java.util.Vector<E>
java.util.Stack<E>

所有已实现的接口: Serializable,Cloneable,

Iterable<E>,
Collection<E>,
List<E>,
RandomAccess
public class Stack<E>
extends Vector<E>
Stack
类表示后进先出(LIFO)的对象堆栈。它通过五个操作对类 Vector 进行了扩展 ,允许将向量视为堆栈。它提供了通常的push 和
pop 操作,以及取堆栈顶点的 peek 方法、测试堆栈是否为空的 empty 方法、在堆栈中查找项并确定到堆栈顶距离的search 方法。

首次创建堆栈时,它不包含项。

Deque
接口及其实现提供了 LIFO 堆栈操作的更完整和更一致的 set,应该优先使用此 set,而非此类。例如:

[code]Deque<Integer> stack = new ArrayDeque<Integer>();


       如果你的应用中涉及到的东西比较耗内存的话,比如:相机、第三方地图、腾讯、新浪、录音、视频播放、大量图片时,如果这些东西同时存在于应用中时,会有很多奇怪的问题出现,自动退出还不报错等等一系列的问题,还有,如果我们的应用中使用startActivity()过多而且并没有及时finish()掉的话,也会出现这样那样的问题,比如:退出应用时没有退出干净,或者莫名其妙的报OOM,启动的服务自动挂起什么的! 
     其实,Google已经提供了一套完整的机制让开发人员控制活动栈与任务栈
        
像这样的跳转我们在开发的过程中算是很长见到的了,在这里我就不贴代码了 ,假如就是有三个活动窗口(Activity1,Activity2,Activity3,)按先后顺序
从Activity1--
startActivity()
到Activity2再到Activity3这个过程大家应该可以想象的到,在这个过程生成的活动堆栈如图所示:



    这个地方说明下,有时候大家可以想着从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值以截图的形式显示,节约时间:

i.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);

i.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)其它:





 
  


[b]一、Activity和Task(栈)的关系
[/b]

 
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录音文件,直接以下面的方式启动去播放录音,猛报错:

      Intent i = new Intent(Intent.ACTION_VIEW); 
      i.putExtra("filePath",path); 
     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是为了让应用更干净,控制更严密,如果加错了标志位,应用是不会报任何错的,只是出现怪异的跳转与关闭!!!


[b]  

[/b]

Stack是一个后进先出(last in first out,LIFO)的堆栈,在Vector类的基础上扩展5个方法而来

Deque(双端队列)比起Stack具有更好的完整性和一致性,应该被优先使用



[plain]
view plaincopy

E push(E item)   
         把项压入堆栈顶部。   
E pop()   
         移除堆栈顶部的对象,并作为此函数的值返回该对象。   
E peek()   
         查看堆栈顶部的对象,但不从堆栈中移除它。   
boolean empty()   
         测试堆栈是否为空。    
int search(Object o)   
         返回对象在堆栈中的位置,以 1 为基数。  

Stack本身通过扩展Vector而来,而Vector本身是一个可增长的对象数组( a growable array of objects)那么这个数组的哪里作为Stack的栈顶,哪里作为Stack的栈底?

答案只能从源代码中寻找,jdk1.6:

[java]
view plaincopy

public class Stack<E> extends Vector<E> {  
    /** 
     * Creates an empty Stack. 
     */  
    public Stack() {  
    }  
  
    /** 
     * Pushes an item onto the top of this stack. This has exactly 
     * the same effect as: 
     * <blockquote><pre> 
     * addElement(item)</pre></blockquote> 
     * 
     * @param   item   the item to be pushed onto this stack. 
     * @return  the <code>item</code> argument. 
     * @see     java.util.Vector#addElement 
     */  
    public E push(E item) {  
    addElement(item);  
  
    return item;  
    }  
  
    /** 
     * Removes the object at the top of this stack and returns that 
     * object as the value of this function. 
     * 
     * @return     The object at the top of this stack (the last item 
     *             of the <tt>Vector</tt> object). 
     * @exception  EmptyStackException  if this stack is empty. 
     */  
    public synchronized E pop() {  
    E   obj;  
    int len = size();  
  
    obj = peek();  
    removeElementAt(len - 1);  
  
    return obj;  
    }  
  
    /** 
     * Looks at the object at the top of this stack without removing it 
     * from the stack. 
     * 
     * @return     the object at the top of this stack (the last item 
     *             of the <tt>Vector</tt> object). 
     * @exception  EmptyStackException  if this stack is empty. 
     */  
    public synchronized E peek() {  
    int len = size();  
  
    if (len == 0)  
        throw new EmptyStackException();  
    return elementAt(len - 1);  
    }  
  
    /** 
     * Tests if this stack is empty. 
     * 
     * @return  <code>true</code> if and only if this stack contains 
     *          no items; <code>false</code> otherwise. 
     */  
    public boolean empty() {  
    return size() == 0;  
    }  
  
    /** 
     * Returns the 1-based position where an object is on this stack. 
     * If the object <tt>o</tt> occurs as an item in this stack, this 
     * method returns the distance from the top of the stack of the 
     * occurrence nearest the top of the stack; the topmost item on the 
     * stack is considered to be at distance <tt>1</tt>. The <tt>equals</tt> 
     * method is used to compare <tt>o</tt> to the 
     * items in this stack. 
     * 
     * @param   o   the desired object. 
     * @return  the 1-based position from the top of the stack where 
     *          the object is located; the return value <code>-1</code> 
     *          indicates that the object is not on the stack. 
     */  
    public synchronized int search(Object o) {  
    int i = lastIndexOf(o);  
  
    if (i >= 0) {  
        return size() - i;  
    }  
    return -1;  
    }  
  
    /** use serialVersionUID from JDK 1.0.2 for interoperability */  
    private static final long serialVersionUID = 1224463164541339165L;  
}  

通过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类为线程安全类(多个方法调用而产生的数据不一致问题属于原子性问题的范畴)

[java]
view plaincopy

public class Test {  
    public static void main(String[] args) {  
        Stack<String> s = new Stack<String>();  
        System.out.println("------isEmpty");  
        System.out.println(s.isEmpty());  
        System.out.println("------push");  
        s.push("1");  
        s.push("2");  
        s.push("3");  
        Test.it(s);  
        System.out.println("------pop");  
        String str = s.pop();  
        System.out.println(str);  
        Test.it(s);  
        System.out.println("------peek");  
        str = s.peek();  
        System.out.println(str);  
        Test.it(s);  
        System.out.println("------search");  
        int i = s.search("2");  
        System.out.println(i);  
        i = s.search("1");  
        System.out.println(i);  
        i = s.search("none");  
        System.out.println(i);  
    }  
      
    public static void it(Stack<String> s){  
        System.out.print("iterator:");  
        Iterator<String> it = s.iterator();  
        while(it.hasNext()){  
            System.out.print(it.next()+";");  
        }  
        System.out.print("\n");  
    }  
}  

结果:

[sql]
view plaincopy

------isEmpty  
true            
------push  
iterator:1;2;3;    
------pop  
3       --栈顶是数组最后一个  
iterator:1;2;  
------peek  
2       --pop取后删掉,peek只取不删  
iterator:1;2;  
------search      
1       --以1为基数,即栈顶为1  
2       --和栈顶见的距离为2-1=1  
-1      --不存在于栈中  

Stack并不要求其中保存数据的唯一性,当Stack中有多个相同的item时,调用search方法,只返回与查找对象equal并且离栈顶最近的item与栈顶间距离(见源码中search方法说明)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Android