栈和队列在安卓中的运用

栈和队列的定义

队列都是一种基本的数据结构,可以用来存放数据,的顺序是先入后出,而队列的顺序是先入先出,我们可以通过它们实现不同很多特定的功能

比如在安卓中,遍历一个布局文件里所有子view,并打印它们。

首先我们先思考一下这个问题,在Android的布局文件的结构,我们可以把它类比做数据结构中的多叉树。

布局文件结构

上图就是一个常见的RootView的结构,我们可以通过getChildAt(int)这个方法,来遍历这个RootView并获取它的所有子View。

方法一:递归

当我们想要遍历一个ViewGroup所有子View的时候,我们通常想到的就是通过递归,而递归也是最容易写出来的。
直接上代码:

fun printAllChildren(rootView : View){
	print(rootView)
	if(rootView is ViewGroup){
		for(index in 0 until rootView.childCount) {
			val view = rootView.getChildAt(index)
			printAllChildren(view)
		}
	}
}

递归的优点很明显,代码清晰,浅显易懂,但是缺点也同样明显,就是当RootView的层级较深的时候,可能会引起栈溢出,而程序会抛出StackOverflowError这个错误。这是因为在JVM中,每当启动一个新的线程,JVM都会为为其分配一个java栈,而每调用一个方法,都会被封装成一个栈帧,进行压栈的操作;当方法执行完毕,就会进行弹栈的操作。
StackOverflowError这个错误又是怎么产生的呢,这是因为在每个栈帧中,都会保存该方法中的一些局部变量、返回地址等数据,这些都是会占用内存,而递归每调用一次方法,就会压栈产生一个新的栈帧并消耗内存,但是一直到最后不会再调用新方法,才会执行弹栈的操作,所以有可能在执行的过程当中超过了内存的限制,就报出StackOverflowError

所以我们需要考虑通过其他的方法来遍历ViewGroup。

方法二:栈和队列

之前我们说过,安卓的布局结构其实就是一个多叉树,而在数据结构中遍历多叉树有两种方式,广度优先深度优先。如下图:
在这里插入图片描述

2.1 广度优先

广度优先其实就是对多叉树的每一层节点依次访问,当一层访问完毕,再进入下一层。就是遵循树的层数,一层一层地遍历。
广度优先算法,是非常适合先入先出的队列来实现的,每层子View依次进入队列,然后取出队头的子View并处理,如果该子View是ViewGroup,那么再取出它所有的子View放入队尾。
所以上图如果通过广度优先来遍历,那么最后的结果就是:A -> B -> C -> D -> E -> F,通过图来看一下:
在这里插入图片描述

java中,可以使用LinkedList来完成队列的功能,因为它实现了Deque双端队列接口,代码实现:

fun printAllChildren(rootView : View){
	val viewDeque = LinkedList<View>()
  	var view = root
  	viewDeque.addLast(view)
  	while(viewDeque.isNotEmpty()){
  		view = viewDeque.poll()	//取出队列第一个元素
  		print(view)
  		if(view is ViewGroup){
  			for(index in 0 until view.childCount){
  				val childView = view.getChildAt(index)
  				viewDeque.addLast(childView)
  			}
  		}
  	}
|

2.2 深度优先

深度优先则是对每个分支的路径,深入访问到叶子节点之后,才会访问其他的节点。
深度优先算法,是非常适合先入后出的栈来实现,对一个子View先做压栈操作,再弹栈,输出该View,然后遍历得到该子View所有的子View并做压栈操作,再弹栈输出栈顶的子View,重复上述的操作。
所以如果通过深度优先算法来遍历,那么最后得到的结果就是A -> C -> F -> B -> E -> D, 通过图来看一下:
在这里插入图片描述

我们还是用LinkedList来实现栈,因为LinkedList有push()pop()方法来模仿压栈弹栈的操作,代码如下:

fun getAllChildren(rootView: View){
	val viewStack = LinkedList()
	var view = rootView
	viewStack.push(view)
	while(viewStack.isNotEmpty()){
		view = viewStack.pop()	//弹出栈顶的元素
		print(view)
		if(view is ViewGroup){
			for(index in 0 until view.getChildCount()){
				val childView = view.getChildAt(index)
				viewStack.push(childView)
			}
		}
	}
}

总结

我们通过三种方法来实现了ViewGroup的遍历,明白了ViewGroup其实就是一种多叉树的数据结构,也明白了栈和队列在安卓中的运用,当然上面的场景也可以变换,比如统计一个ViewGroup所有子View的数量自己实现一个方法查找指定id的子View等等。

转载请注明出处

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值