Java实现基本数据结构——数组

2023-05-16

数组概念

所谓数组,是有序的元素序列。 若将有限个类型相同的变量的集合命名,那么这个名称为数组名。 在数据结构中,数组是一种线性表,就是数据排列成一条直线一样的结构。在内容空间中,数组的表现是一块连续的内存和储存有相同的数据类型。正因为这个特性,数组可以实现通过索引下标,在O(1)的时间复杂度内快速检索某个数据,这就是“随机访问”。但是由于内存空间是连续的,所以数组在进行插入和删除操作时,就需要对数据进行维护,进行大量的数据搬移工作。

Java中的数组

Java中的基础数组是一种静态数组,在创建的时候空间就是固定的,后期无法进行扩容或者缩容。 其创建方法如下 int[] arr = new int[10]; //创建一个容量为10的int型数组 String[] strArr = new String[10] //创建一个容量为10的字符串数组;

基础数组其实并不符合数据结构中对于数组的定义,因为基础数组无法自动扩容和缩容。 在JDK中,其实也实现了可以动态扩容的数组,比如常用的java.util.ArrayList这个类,就是一个动态的泛型数组类。 泛型:泛型是程序设计语言的一种特性。允许程序员在强类型程序设计语言中编写代码时定义一些可变部分,那些部分在使用前必须作出指明。比如在ArrayList类中,我们实例化的时候,需要指定存储的数据类型,这个类本身是可以接收实例化时指定的任意数据类型(基础数据类型除外,只能使用基础数据类型的包装类)。

Java语言实现数组类

在本文中,我们将从数据的设计开始,抛弃JDK已经实现的ArrayList类,重新通过Java语言实现一个数组的数据结构类,从底层理解这个数据结构的原理与思想。

设计一个静态整型数组类

首先,我们先设计一个静态的数组,以int数组为例。不考虑扩容,先从最简单的类来理解数组的基本功能。 数组可以表示为下图:

上图代表一个容量为7,里面每个空间存储了一个int类型的 1 ,这样一个数组。 数组最大的优点就是通过索引值快速查询数据,比如array[2]可以快速查询到第三个空间中的数据 1 。根据这个优点,我们可以想到,数组比较适合存储索引有语义的数据,比如成绩单这种,我们把名次当做索引,分数当做数据用一个数组存储起来,就可以快速获取某个名次的分数。 按照这个数组,我们可以设计一个数组类,首先设计好数组的功能:

  1. 要能获取数组中的数据个数。
  2. 要能获取数组的容量。
  3. 要能判断数组是否为空。
  4. 要能向数组中插入元素
  5. 要能从数组中查询元素
  6. 要能从数组中删除元素

根据上述功能,我们先进行代码的编写实现这个类的前三个功能:获取元素个数,获取数组容量,判断数组是否为空(在后续的代码中,我们将省略Java的包名,直接对类的主体进行展示):

public class Array {

	private int[] data; // 定义一个基础数组,用来存放数据。
	private int size;  // 用记录数组中的数据个数。

	// 构造函数,实例化的时候需要指定一个容量capacity对数组进行初始化
	public Array(int capacity) {
		data = new int[capacity];
		size = 0;
	}
	
	// 无参构造,实例化时不指定容量将默认为10
	public Array() {
		this(10);  // 调用有参构造函数
	}

	// 获取数组中元素的个数
	public int getSize() {
		return size;
	}

	// 获取数组的容量
	public int getCapacity() {
		return data.length;  // 这里不可以直接return capacity,因为capacity不是成员变量。
	}

	// 判断当前数组是否为空
	public boolean isEmpty() {
		return size == 0;
	}
	
}

到这里,我们已经封装了一个Array数组类,实现了一部分基础功能,下面我们将实现向数组中添加元素的功能。向数组中添加元素的原理如下图所示:

我们想把77这样一个元素插入到索引为1的位置上,作为数组这样一种数据类型来讲,要想在数组中间插入元素,需要将插入位置开始的元素,全部向后面移动一个位置,这是数组的性质。

 如上图所示,我们需要将88,99,100三个元素从索引1,2,3的位置,挪动到索引2,3,4位置上,再将77插入到索引1的位置上。 在这一过程中,我们需要注意的点有以下几点:

  1. 数组的容量是否已满,如果数组已经满了,就没有办法向后挪动,再插入元素了。
  2. 插入的位置一定是数组的有效位置。理解size和data.length的区别,一个是用户能看到的长度,一个是数组的容量,data.length 减 size这一部分的元素,用户是看不到的,我们对用户屏蔽。
  3. 每次插入后,数组的size指针需要进行维护,向后移动一个距离,代表元素数+1.

我们将这一过程变成代码(针对某一个函数,我们只展示该函数的代码块,在章节末尾再将所有代码整合进类中,以此减少文章的行数,更有利于阅读):

public class Array {

	// 向数组中索引为index的位置插入一个元素
	public void add(int index, int e) {
		// 如果数组已经满了,就抛出异常,添加失败
		if(size == data.length)  
			throw new IllegalArgumentException("Array is full.");
		// 如果索引值小于0或者索引值大于了数组元素的个数,抛异常
		if (index < 0 || index > size) 
			throw new IllegalArgumentException("Wrong index.");
		// 将index位置开始的元素向后移动一格
		for (int i = size - 1 ; i >= index ; i--) 
			data[i + 1] =data[i];
		// 将e插入到index索引位上
		data[index] = e;
		// 维护元素个数+1
		size ++;
	}

	// 一个将元素e快速插入数组头部方法
	public void addFirst(int e) {
		add(0, e);
	}
	
	// 一个将元素e快速插入数组尾部的方法
	public void addLast(int e) {
		add(size, e);
	}
	
}

至此,我们完成了数组添加元素的操作,下面我们来完成从数组中查询和修改元素的操作:

  1. 查询索引为index位置的元素:根据数组的性质,直接返回data[index]的值就可以。
  2. 查询元素e是否存在在数组中:数组中查询是否存在元素e,就需要对数组进行遍历。
  3. 修改索引为index位置的元素:直接将data[index]进行更新即可,这是java基础数组的操作方法。
  4. 从索引为index位置删除元素:该操作和插入元素可以看做是相反的操作,我们将一个元素删除,其实就是将index之后的元素前移一位,index位置的元素就被覆盖掉了。操作后对size进行维护,size–。

代码实现:

public class Array {

	// 查询索引为index位置的元素e
	public int get(int index) {
		// 如果索引值小于0或者索引值大于了数组元素的个数,参数不合法,抛异常
		if (index < 0 || index > size) 
			throw new IllegalArgumentException("Wrong index.");
		return data[index];
	}

	// 查询数组中是否存在元素e
	public boolean isExist(int e) {
		for (int i = 0; i < size; i++) 
		{
			if (data[i] == e)
				return true;
		}
		return false;
	}

	// 查询元素e在数组中的位置
	public int findIndex(int e) {
		for (int i = 0; i < size; i++) 
		{
			if (data[i] == e) 
				return i;
		}
		// 没有找到元素e就返回-1
		return -1;
	}

	// 从数组中移除索引为index位置的元素,并将该元素返回
	public int remove(int index) {
		if (index < 0 || index > size) 
			throw new IllegalArgumentException("Wrong index");
		// 记录data[index]元素,最后返回变量ret
		int ret = data[index];
		// 从index+1开始往前覆盖数组
		for (int i = index + 1; i < size; i++) 
			data[i-1] = data[i];
		size --;
		return ret;
	}
	
	// 一个快速删除头部元素的方法
	public int removeFirst() {
		remove(0);
	}
	
	// 一个快速删除尾部元素的方法
	public int removeLast() {
		remove(size);
	}
	
	// 从数组中删除元素e
	public void removeElement(int e) {
		int index = findIndex(e);
		if (index != -1) 
			remove(index);
	}
	
}

OK!写到这里,我们就完成了这个数组类的所有基础功能,当然这些功能有一些可能在实际中并不常用,我们在学习的时候还是以了解底层实现思想为主,就写的比较详细。 最后,我们将重写一下toString函数,来完成数组的输出功能:

public class Array {

	@Override
	public String toString() {
		StringBuilder res = new StringBuilder();
		res.append(String.format("Array: size = %d, capacity = %d\n", size, data.length));
		res.append('[');
		for (int i = 0; i < size - 1; i++) {
			res.append(data[i]);
			if (i != size) 
				res.append(", ");
		}
		res.append(']');
		return res.toString();
	}
	
}

好了,我们将所有代码整合起来,就完成了我们通过Java代码实现的一个静态int型数组,代码如下:

public class Array {

	private int[] data; // 定义一个基础数组,用来存放数据。
	private int size;  // 用记录数组中的数据个数。

	// 构造函数,实例化的时候需要指定一个容量capacity对数组进行初始化
	public Array(int capacity) {
		data = new int[capacity];
		size = 0;
	}
	
	// 无参构造,实例化时不指定容量将默认为10
	public Array() {
		this(10);  // 调用有参构造函数
	}

	// 获取数组中元素的个数
	public int getSize() {
		return size;
	}

	// 获取数组的容量
	public int getCapacity() {
		return data.length;  // 这里不可以直接return capacity,因为capacity不是成员变量。
	}

	// 判断当前数组是否为空
	public boolean isEmpty() {
		return size == 0;
	}
	
	// 向数组中索引为index的位置插入一个元素
		public void add(int index, int e) {
			// 如果数组已经满了,就抛出异常,添加失败
			if(size == data.length)  
				throw new IllegalArgumentException("Array is full.");
			// 如果索引值小于0或者索引值大于了数组元素的个数,抛异常
			if (index < 0 || index > size) 
				throw new IllegalArgumentException("Wrong index.");
			// 将index位置开始的元素向后移动一格
			for (int i = size - 1 ; i >= index ; i--) 
				data[i + 1] =data[i];
			// 将e插入到index索引位上
			data[index] = e;
			// 维护元素个数+1
			size ++;
		}

		// 一个将元素e快速插入数组头部方法
		public void addFirst(int e) {
			add(0, e);
		}
		
		// 一个将元素e快速插入数组尾部的方法
		public void addLast(int e) {
			add(size, e);
		}
		
		// 查询索引为index位置的元素e
		public int get(int index) {
			// 如果索引值小于0或者索引值大于了数组元素的个数,参数不合法,抛异常
			if (index < 0 || index > size) 
				throw new IllegalArgumentException("Wrong index.");
			return data[index];
		}

		// 查询数组中是否存在元素e
		public boolean isExist(int e) {
			for (int i = 0; i < size; i++) 
			{
				// 泛型类中的值比较需要用equals方法
				if (data[i].equals(e))
					return true;
			}
			return false;
		}

		// 查询元素e在数组中的位置
		public int findIndex(int e) {
			for (int i = 0; i < size; i++) 
			{
				// 泛型类中的值比较需要用equals方法
				if (data[i].equals(e)) 
					return i;
			}
			// 没有找到元素e就返回-1
			return -1;
		}

		// 从数组中移除索引为index位置的元素,并将该元素返回
		public int remove(int index) {
			if (index < 0 || index > size) 
				throw new IllegalArgumentException("Wrong index");
			// 记录data[index]元素,最后返回变量ret
			int ret = data[index];
			// 从index+1开始往前覆盖数组
			for (int i = index + 1; i < size; i++) 
				data[i-1] = data[i];
			size --;
			return ret;
		}
		
		// 一个快速删除头部元素的方法
		public int removeFirst() {
			return remove(0);
		}
		
		// 一个快速删除尾部元素的方法
		public int removeLast() {
			return remove(size);
		}
		
		// 从数组中删除元素e
		public void removeElement(int e) {
			int index = findIndex(e);
			if (index != -1) 
				remove(index);
		}
		
		@Override
		public String toString() {
			StringBuilder res = new StringBuilder();
			res.append(String.format("Array: size = %d, capacity = %d\n", size, data.length));
			res.append('[');
			for (int i = 0; i < size - 1; i++) {
				res.append(data[i]);
				if (i != size) 
					res.append(", ");
			}
			res.append(']');
			return res.toString();
		}
	
}

最后我们创建一个测试用例来测试一下我们自己实现的Array类:

public class Main {

	public static void main(String[] args) {
		
		Array arr = new Array(8);
		// 从数组尾部依次插入0-7
		for (int i = 0; i < 8; i++) {
			arr.addLast(i);
		}
		System.out.println(arr.toString());
		
		arr.remove(5);
		System.out.println(arr.toString());
		
		arr.removeElement(2);
		System.out.println(arr.toString());
		
	}

}

输出结果为:

 可以看到,我们这个数组成功实现了插入,指定位置移除与指定元素移除的操作。 静态数组的讲解到此就结束了。

实现泛型

我们前文所创建的Array类是一个只能存储int元素的数组,如果我们想要存储String类型呢? 此时,Java中的泛型机制就可以很好的帮助到我们。泛型的写法很简单,只需要在类名后面加上<>,尖括号中填写任意一个大写字符,比如一般写成,就是声明一个泛型类。此时,E表示的就是派生自Object的任意类。我们在实例化该类时,就需要指定存储的数据类型。(所以此时无法输入基本数据类型如int,只能使用包装类Integer。) 需要注意的是,在泛型类中,由于有些类无法使用 == 的方式对值进行相等比较(比如对象的引用),在isExist()和findIndex()函数中,我们需要用equals方法进行值的比较:

data[i].equals(e)

我们将上文的Array类的一部分代码拿出来作为示例,将它泛型化,相信大家可以很快的理解泛型类的实现。

public class Array<E> { //泛型

		private E[] data; // 定义一个存储E类型元素的数组,用来存放数据。
		private int size;  // 用记录数组中的数据个数。

		// 构造函数
		public Array(int capacity) {
			// 由于JAVA中不支持泛型数组,我们需要用对Object数组造型的方法来达到泛型的目的
			data = (E[])new Object[capacity];
			size = 0;
		}
		
		// 向数组中索引为index的位置插入一个元素e
		public void add(int index, E e) {
			if(size == data.length)  
				throw new IllegalArgumentException("Array is full.");
			if (index < 0 || index > size) 
				throw new IllegalArgumentException("Wrong index.");
			for (int i = size - 1 ; i >= index ; i--) 
				data[i + 1] =data[i];
			data[index] = e;
			size ++;
		}
		
	}

我们可以看到,此时我们将int e,这样一种传入参数,全部用E e来替换,这就是泛型类的使用方法。 学会了使用泛型类,下面我们就将数组这样的一种数据结构完整的实现——加入扩容机制。

设计一个动态泛型数组类

之前我们提到过,JDK中已经实现了一个ArrayList这样一个泛型类,平时工作与学习我们可以直接拿来使用。但在本章节中,作为学习,我们将亲自动手设计并实现一个动态数组,来学习数据结构的底层实现。

Java中的数组,其实没有真正意义上的动态性,我们只能通过自己的方法来实现这样一种效果。 最直观和最实用的方法,就是当我们当前的数组容量装满之后,我们新创建一个新的数组,新数组的容量是之前旧数组的x倍,再将旧的数组元素全部拷贝到新数组中,后面我们丢弃旧的数组,维护新的数组就可以。其实JDK中很多的类都使用了这一思想,比如说常用的ArrayList和HashMap。 按照这种思想,我想大家其实已经有了一定的思路,我们只需要给前文完成的静态数组添加进一个扩容函数,每当数组满了,就新建数组拷贝数据,就完成了动态数组的功能。

首先,我们给数组设计一个扩容函数resize();

public class ArrayList<E> {

		private E[] data; // 定义一个基础数组,用来存放数据。
		private int size;  // 用记录数组中的数据个数。
		
		// 数组扩容系数,当数组的元素个数大于等于数组的容量*系数时,进行扩容
		private double resizeRatio = 0.75; 
		
		// 扩容方法,对数组进行扩容,该方法对用户屏蔽,所以私有
		private void resize(int newCapacity) {
			E[] newData = (E[])new Object[newCapacity];
			for (int i = 0; i < size; i++) 
				newData[i] = data[i]; // 拷贝数据
			
			// 将对象的引用更新,此时指针指向了新数组的内存地址
			data = newData;  
		}
		
}

扩容方法写好之后,我们就可以改写我们的插入方法,在之前的静态数组中,如果数组满了,我们就报错。有了扩容方法之后,数组满了我们就扩容,再进行插入操作。改写后的插入方法如下:

public class ArrayList<E> {
	
		private E[] data; // 定义一个基础数组,用来存放数据。
		private int size;  // 用记录数组中的数据个数。
		
		// 数组扩容系数,当数组的元素个数大于等于数组的容量*系数时,进行扩容
		private double resizeRatio = 0.75; 
		
		// 向索引index位置插入一个元素e
		public void add(int index, E e) {
			// 索引不合法就报错
			if (index > size || index < 0) 
				throw new IllegalArgumentException("Wrong index");
			// 如果数组满足:已存元素个数等于元素容量*系数,就扩容,新数组容量为之前的2倍
			if (size == (int)(data.length*resizeRatio)) 
				resize(2 * data.length);
			// 继续进行插入操作
			for (int i = size - 1; i >= index; i--) 
			{
				data[i+1] = data[i];
			}
			data[index] = e;
			size ++;
		}

		// 扩容方法,对数组进行扩容,该方法对用户屏蔽,所以私有
		private void resize(int newCapacity) {
			E[] newData = (E[])new Object[newCapacity];
			for (int i = 0; i < size; i++) 
				newData[i] = data[i]; // 拷贝数据
			
			// 将对象的引用更新,此时指针指向了新数组的内存地址
			data = newData;  
		}

}

完成插入操作之后,相信大家对动态数组的实现思想有了一定的了解,而查询、修改与删除操作,在动态数组中,其实与静态数组没有什么不同。为了更高效利用空间,我们可以在删除元素后,对数组进行缩容,来节约内容空间。动态数组类的全部代码如下:

public class ArrayList<E> {

		private E[] data; // 定义一个基础数组,用来存放数据。
		private int size;  // 用记录数组中的数据个数。
		
		// 数组扩容系数,当数组的元素个数大于等于数组的容量*系数时,进行扩容
		private double resizeRatio = 0.75; 
		
		public ArrayList() {
			this(10);
			// TODO Auto-generated constructor stub
		}
		
		public ArrayList(int capacity) {
			data = (E[])new Object[capacity];
			size = 0;
		}
		
		public int getSize() {
			return size;
		}
		
		public int getCapacity() {
			return data.length;
		}
		
		public boolean isEmpty() {
			return (size == 0);
		}
		
		// 向索引index位置插入一个元素e
		public void add(int index, E e) {
			// 索引不合法就报错
			if (index > size || index < 0) 
				throw new IllegalArgumentException("Wrong index");
			// 如果数组满足:已存元素个数等于元素容量*系数,就扩容,新数组容量为之前的2倍
			if (size == (int)(data.length*resizeRatio)) 
				resize(2 * data.length);
			// 继续进行插入操作
			for (int i = size - 1; i >= index; i--) 
			{
				data[i+1] = data[i];
			}
			data[index] = e;
			size ++;
		}

		// 扩容方法,对数组进行扩容,该方法对用户屏蔽,所以私有
		private void resize(int newCapacity) {
			E[] newData = (E[])new Object[newCapacity];
			for (int i = 0; i < size; i++) 
				newData[i] = data[i]; // 拷贝数据
			
			// 将对象的引用更新,此时指针指向了新数组的内存地址
			data = newData;  
		}
		
		public void addFirst(E e) {
			add(0, e);
		}
		
		public void addLast(E e) {
			add(size, e);
		}
		
		public E get(int index) {
			if (index < 0 || index > size) 
				throw new IllegalArgumentException("Wrong index");
			return data[index];
		}
		
		public void set(int index, E e) {
			if (index < 0 || index > size) 
				throw new IllegalArgumentException("Wrong index");
			data[index] = e;
		}
		
		// 查询数组中是否存在元素e
		public boolean  isExist(E e) {
			for (int i = 0; i < size; i++) 
			{
				if (data[i].equals(e)) 
				{
					return true;
				}
			}
			return false;
		}

		// 查询元素e在数组中的位置
		public int findIndex(E e) {
			for (int i = 0; i < size; i++) 
			{
				if (data[i].equals(e) ) 
				{
					return i;
				}
			}
			return -1;
		}
		
		// 从数组中移除索引为index位置的元素,并将该元素返回
		// 缩容后,如果数组元素个数满足条件,就进行缩容
		public E remove(int index) {
			if (index < 0 || index > size) 
				throw new IllegalArgumentException("Remove failed");
			E ret = data[index];
			for (int i = index + 1; i < size; i++) 
			{
				data[i-1] = data[i];
			}
			size --;
			data[size] = null;
			
			// 如果数组的空间有一半没用就缩容为原来的1/2
			if (size == data.length / 2) 
				resize(data.length/2);
			
			return ret;
		}
		
		public E removeFirst() {
			return remove(0);
		}
		
		public E removeLast() {
			return remove(size - 1);
		}
		
		public void removeElement(E e) {
			int index = findIndex(e);
			if (index != -1) 
				remove(index);
		}
		
		@Override
		public String toString() {
			// TODO Auto-generated method stub
			StringBuilder res = new StringBuilder();
			res.append(String.format("Array: size = %d, capacity = %d\n", size, data.length));
			res.append('[');
			for (int i = 0; i < size; i++) {
				res.append(data[i]);
				if (i != size -1) 
				{
					res.append(", ");
				}
			}
			res.append(']');
			return res.toString();
		}

}

最后,我们写一个测试用例来对我们实现的动态数组类进行测试:

public class Main {

	public static void main(String[] args) {
		
		ArrayList<Integer> arr = new ArrayList<Integer>(8);
		// 从数组尾部依次插入0-6
		for (int i = 0; i < 6; i++) {
			arr.addLast(i);
		}
		System.out.println(arr.toString());

		
		arr.addLast(6);
		System.out.println(arr.toString());
		
		
	}

}

执行结果:

可以看到,数组自动进行了容量的扩充。

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java实现基本数据结构——数组 的相关文章

  • 解决Android Studio 无法通过gradle 下载https://dl.google.com/android/repository/addons_list-3.xml 解决办法

    安卓gradle的时候 xff0c 会弹出来这样报错 Task prepareKotlinBuildScriptModel UP TO DATE IOException https dl google com android reposit
  • libGL error: MESA-LOADER: failed to open vmwgfx (search paths /usr/lib/x86_64-linux-gnu/dri)

    使用emulator use system libs 命令 Using the libstdc 43 43 so 6 that is available in your system instead of the one bundled w
  • Java内部类详解

    一 内部类基础 在Java中 xff0c 可以将一个类定义在另一个类里面或者一个方法里面 xff0c 这样的类称为内部类 广泛意义上的内部类一般来说包括这四种 xff1a 成员内部类 局部内部类 匿名内部类和静态内部类 下面就先来了解一下这
  • 使用Ubuntu18.04编译android8.1

    使用虚拟机中的Ubuntu18 04编译android8 1 1 软硬件要求 1 1 硬件 16G的内存 xff1b 200G的存储盘 1 2 软件 Ubuntu18 04 这样的软硬件要求并非必须 xff0c 但是经过测试这样的配置刚好能
  • Android 8 细分版本 分支 以及支持的设备

    Android 8 细分版本 分支 以及支持的设备 细分版本分支版本支持的设备OPM8 181005 003android 8 1 0 r48OreoPixel COPM7 181005 003android 8 1 0 r47OreoNe
  • Android 打印堆栈日志的几种方法

    在Android调试过程中经常会出现程序出现的结果不是我们预期的结果 xff0c 那就需要加Log打印调试 xff0c 看调用过程是否正确 xff0c 此时就需要打印程序的调用栈 xff0c 特别是Android代码相当庞大 xff0c 打
  • 在 Ubuntu 18.04 上安装 SmartGit

    在开始安装之前 xff0c 很有趣 确保我们系统中的所有软件包都是最新的 为此 xff0c 在终端 Ctl 43 Alt 43 T 中 xff0c 我们只需编写 xff1a 1 sudo apt update sudo apt upgrad
  • 复工第一事:干掉 Notepad++

    点击上方 芋道源码 xff0c 选择 设为星标 管她前浪 xff0c 还是后浪 xff1f 能浪的浪 xff0c 才是好浪 xff01 每天 10 33 更新文章 xff0c 每天掉亿点点头发 源码精品专栏 原创 Java 2021 超神之
  • Ubuntu18.04编译Android8.0系统源码

    首先 需要一个台式电脑 xff0c 有个i7处理器 xff0c 有一个1T的机械 43 500G的固态 xff0c 如果条件允许改一个服务器也可以 我这里是一个台式电脑 在台式电脑上安装一个虚拟机 xff0c 基本是使用的VMware xf
  • m, mm以及mmm编译命令以及make snod的使用

    1 xff09 编译指定Package Android源码目录下的build envsetup sh文件 xff0c 描述编译的命令 croot 切到Android源码树的根目录 当你深入Android源码树的子目录 xff0c 想回到根目
  • 复杂条件逻辑的梳理

    为什么会感觉有些需求无从下手 在产品需求梳理或者业务逻辑调研阶段 xff0c 有时会遇到产品需求无从下手的问题 xff0c 分析下来 xff0c 一般情况如下 xff1a 需求边界不明确 xff0c 输入和输出的界定不清晰 xff0c 无法
  • Android源码刷机步骤

    打开OEM开关 xff1a 先点击设置 关于手机 版本号七次 开发者选项 打开OEM解锁 xff08 这步必须可以上网 xff0c 否则打不开 xff09 进入bootloader页面 使用方法1必须安装adb platform tools
  • Android Studio导入和调试Android8.0源码

    生成IDE相关文件 idegen专门为IDE环境调试源码而设计的工具 xff0c 依次执行如下命令 xff1a source build envsetup sh mmm development tools idegen developmen
  • make snod注意事项-刷机后启动异常

    1 正确执行顺序 需要执行 source build envsetup sh lunch 2 单独编译 xff0c 刷机后运行异常 全编andorid后 xff0c 单独修改编译一个framwork模块 xff0c make snod会有如
  • adb remount 系统提示只读文件系统Read-only file system,解决用adb disable-verity

    在Android6 0 xff08 Android M xff09 userdebug版本上 eng版本不存在该问题 xff0c 发现使用adb remount 系统之后 xff0c 还是不能对system分区进行操作 xff0c 提示没有
  • 枚举 switchcase 标签必须为枚举常量的非限定名称

    enum switch case label must be the unqualified name of an enumeration constant 或 错误 枚举 switchcase 标签必须为枚举常量的非限定名称case Co
  • VMware为什么会越用占用的内存越大?该如何清理?

    现象描述 xff1a VMware用了一段时间后发现原来刚开始只占5G左右的内存 xff0c 慢慢的会占用几十个G xff0c 甚至更多 xff0c 磁盘空间占用越来越大 解决办法 xff1a 虚拟机内部执行cat dev zero gt
  • H264中的时间戳(DTS和PTS)

    xff08 1 xff09 Ffmpeg中的DTS 和 PTS H264里有两种时间戳 xff1a DTS xff08 Decoding Time Stamp xff09 和PTS xff08 Presentation Time Stamp
  • UEFI/Legacy两种启动模式下安装Win10/Ubuntu双系统

    文章目录 更多操作细节请移步到 UEFI Legacy两种启动模式下安装Win10 Ubuntu双系统 http www aigrantli com archives uefilegacy E4 B8 A4 E7 A7 8D E5 90 A
  • H264视频编码原理

    一 为什么要对视频编码 视频是由一帧帧的图像组成 xff0c 就像gif图片一样 一般视频为了不会让人感觉到卡顿 xff0c 一秒钟至少需要16帧画面 一般30帧 加入该视频是一个1280x720的分辨率 xff0c 那么不经过编码一秒钟传

随机推荐

  • H.264基础知识总结

    H264是视频编解码格式 xff1b 学习H264之前首先要搞明白一个问题 xff0c 视频为什么要编码 xff0c 编码传输不行吗 xff1f 视频就是一堆图片按时间顺序播放 xff0c 在编码标准出现之前 xff0c 不经过编码的原始码
  • linux文件分割(将大的日志文件分割成小的)

    linux文件分割 xff08 将大的日志文件分割成小的 xff09 linux下文件分割可以通过split命令来实现 xff0c 可以指定按行数分割和安大小分割两种模式 Linux下文件合并可以通过cat命令来实现 xff0c 非常简单
  • 华为AGC性能管理功能sdk集成

    集成SDK 1 xff09 在AGC网站的我的项目中选择需要启用性能管理的应用 xff0c 点击质量 gt 性能管理 xff0c 进入性能管理服务页面 xff0c 立即开通服务 2 xff09 添加AGC插件 xff0c 在Android
  • Android平台集成华为AGC性能管理服务问题处理指南

    最近尝试集成了华为AGC的性能管理服务 xff0c 集成过程中也遇到一些问题 本文就对我在集成性能管理服务的踩坑记录进行总结 xff0c 希望能帮到大家 问题一 xff1a 刚集成性能管理服务 xff0c 报错miss client id
  • Android ANR全解析&华为AGC性能管理解决ANR案例集

    1 ANR介绍 1 1 ANR是什么 ANR xff0c 全称为Application Not Responding xff0c 也就是应用程序无响应 如果 Android 应用的界面线程处于阻塞状态的时间过长 xff0c 就会触发 应用无
  • JAVA包装类

    什么是包装类 虽然 Java 语言是典型的面向对象编程语言 xff0c 但其中的八种基本数据类型并不支持面向对象编程 xff0c 基本类型的数据不具备 对象 的特性 不携带属性 没有方法可调用 沿用它们只是为了迎合人类根深蒂固的习惯 xff
  • Rxjava理论(一)

    大家都知道RxJava上手是非常难的一个框架 xff0c 为什么说是难呢 xff0c 因为它的功能非常强大 xff0c 各种操作符让人很难上手 xff0c 搭配使用带生命周期的框架有RxLife等 以至于后面出了很多类似Rxjava的框架
  • rxjava理论(二)

    doOnSubscribe的监听 在上一节我们介绍过subscribeOn是控制上游的observable在哪个线程执行 xff0c 关于怎么控制上游的observable可以看我上篇文章RxJava面经一 xff0c 拿去 xff0c 不
  • RxJava Hook(钩子)方法

    Hook技术又叫钩子函数 xff0c 在系统没有调用函数之前 xff0c 钩子就先捕获该消息 xff0c 得到控制权 这时候钩子程序既可以改变该程序的执行 xff0c 插入我们要执行的代码片段 xff0c 还可以强制结束消息的传递 RxJa
  • android底层之什么是Zram?

    ZRAM的理解 ZRAM xff08 压缩内存 xff09 的意思是说在内存中开辟一块区域压缩数据 就是说假设原来150MB的可用内存现在可以放下180MB的东西 本身不会提高内存容量和运行速度 只是让后台程序更少被系统砍掉罢了 xff0c
  • rxjava - compose()操作符

    1 问题背景 想要给多个流重复应用 34 一系列 34 相同的操作符 该怎么办 比如 我们使用Rx 43 Retrofit进行网络请求时 都有遇到这样场景 要在io线程中请求数据 在主线程订阅 更新UI 所以必须频繁使用下面这样的代码 su
  • RxJava2 背压

    1 背压 在RxJava中 xff0c 会遇到被观察者发送消息太快以至于它的操作符或者订阅者不能及时处理相关的消息 xff0c 这就是典型的背压 Back Pressure 场景 BackPressure经常被翻译为背压 xff0c 背压的
  • MVVM实现与原理分析

    1 MVVM简介 1 1 MVC amp MVP amp MVVM MVP MVVM与MVP结构类似 xff0c MVP也是通过Presenter将View与Model解耦 不过MVVM是基于观察者模式 xff0c viewModel不持有
  • PCM数据格式

    什么是PCM PCM全称Pulse Code Modulation xff0c 翻译一下是脉冲调制编码 其实大可以不用关心英文释义 xff0c 之所以这么命名是因为一些历史原因 在音视频中 xff0c PCM是一种用数字表示采样模拟信号的方
  • 音频帧大小的计算

    音频aac mp3文档规定 xff1a AAC xff1a 帧大小1024个sample xff0c 采样率为44100Hz 帧播放时长 xff1a acc dur 61 1024 44100 61 0 02322s 61 23 22ms
  • Java多线程系列--join()

    1 join 介绍 join 定义在Thread java中 join 的作用 xff1a 让 主线程 等待 子线程 结束之后才能继续运行 这句话可能有点晦涩 xff0c 我们还是通过例子去理解 xff1a 主线程 public class
  • Android的设计模式-装饰者模式

    1 定义 动态地给一个对象添加一些额外的职责 就增加功能来说 xff0c 装饰模式相比生成子类更为灵活 2 介绍 装饰者模式属于结构型模式 装饰者模式在生活中应用实际上也非常广泛 xff0c 一如一间房 xff0c 放上厨具 xff0c 它
  • android音频焦点Audio Focus

    为了便于理解 xff0c 我们以android的8 0以前的版本为例 xff0c 8 0以后有一定改动 xff0c 但是基本思路一样 关于管理音频焦点 xff08 8 0以前和更高版本 xff09 的官方文档 xff1a https dev
  • Android音频通路的切换

    Android支持多种设备的的输出 一台正常的机子 xff0c 本身就自带话筒 xff0c 扬声器 xff0c 麦克风等多个声音输入输出设备 xff0c 再加上五花八门的外置设备 xff08 通过耳机 xff0c 蓝牙 xff0c wifi
  • Java实现基本数据结构——数组

    数组概念 所谓数组 xff0c 是有序的元素序列 若将有限个类型相同的变量的集合命名 xff0c 那么这个名称为数组名 在数据结构中 xff0c 数组是一种线性表 xff0c 就是数据排列成一条直线一样的结构 在内容空间中 xff0c 数组