数组的概念
概念
数组: 是多个相同类型数据按照一定排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理
数组中的概念
数组名: 数组的名称,命名
下标: 从0开始
元素:数组中存储的数据
数组的长度: 数组的存储了多少个元素
特点
- 数组中的元素在内存中是依次紧密排列的,有序的
- 数组属于引用数据类型的变量,但是数组的元素 - 既可以是基本数据类型,也可以是引用数据类型
- 数组一旦初始化了,数组的长度就固定了,不能再更改了(声明了就申请了内存大小)
- 创建数组会在内存中开辟一整块连续空间,占据空间的大小,取决于数组的长度和数组中元素的类型
分类
按照元素类型:基本数据类型元素数组;引用数据类型数组
按照数组的维度:一维数组;二维数组;....
一维数组
数组的定义与初始化
public class Main {
public static void main(String[] args) {
// 声明1
int[] intArray;
// 赋值
intArray = new int[10];
// 声明2
double[] doubleArray;
// 赋值2
doubleArray = new double[]{1.0, 2.0, 3.0};
// 声明+赋值一起初始化
// 动态初始化: 数组变量的赋值和数组元素的赋值分开进行
int[] arrayInt = new int[10];
arrayInt[0] = 1;
// 静态初始化:数组变量的赋值和数组的元素赋值同时进行
String[] stringArray = new String[]{"测试1", "测试2"};
Double[] doubleArrayDouble = {1.0, 2.0, 3.0};
// 数组的取值
System.out.println(doubleArrayDouble[0]);
System.out.println(doubleArrayDouble[1]);
System.out.println(doubleArrayDouble[2]);
System.out.println(doubleArrayDouble[3]); // 超出数组长度,报错:ArrayIndexOutOfBoundsException
// 数组的length与遍历
for(int i = 0; i < doubleArrayDouble.length; i++) {
System.out.println(doubleArrayDouble[i]);
}
}
}
不同类型数组的初始值
// 数组默认值
// 整型数组元素的默认初始化值
int[] intArrary1 = new int[10];
System.out.println(intArrary1[0]); // 0
// 浮点型数组元素的默认初始化值
double[] doubleArrayDouble1 = new double[10];
System.out.println(doubleArrayDouble1[0]); // 0.0
// 字符型数组元素默认初始值
char[] charArray = new char[10];
System.out.println(charArray[0]); // 数字0
if(charArray[0] == 0) {
System.out.println("是数字0");
}
if(charArray[0] == '0') {
System.out.println("是字符0");
}
boolean[] booleanArray = new boolean[10];
System.out.println(booleanArray[0]); // false
String[] stingArray = new String[10];
System.out.println(stingArray[0]); // null
java虚拟机的内存划分
为了提高运算效率,对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方法
虚拟机栈: 用于存储正在执行的每个java方法的局部变量表等,局部变量存放了编译期可知长度的各种数据类型,对象引用,方法执行完,自动释放。
堆内存: 存储对象(包括数组对象),new来创建的,都存储在堆内存
方法区: 存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据
本地方法栈:当程序中调用了native的本地方法时,本地方法执行期间的内存区域
程序计数器:程序计数器是CPU中的寄存器,他包含每个线程下一条要执行的指令的地址
存储结构
和前端相似,也是栈和堆,栈中存储的名和地址值,地址值指向堆
虚拟机栈: 存放的数组名称
堆: 存放的数组的实体
案例一
案例二
综上可以看出,和前端存储和修改是一致的,如案例二,修改test2,test1的值会同步修改;
同理: 数组的比较,比较的是地址值 intTest == intTest2 ture;
二维数组
二维数组: 可以看成一维数组又作为另外一个一堆数组array2的元素存在,其实从数组的底层运行机制来看,是没有多维数组的
数组的定义与初始化
class TwoDimensionarArray {
public static void main(String[] args) {
// 静态初始化:赋值和元素赋值同时进行
int[][] intTest = new int[][]{{1,2,3}, {2,3,4}, {3,4,5}};
// 动态初始化
String[][] stringTest = new String[3][3];
String[][] stringTest2 = new String[3][];
// 其他正确方式
int arrAry[][] = new int[][]{{1,2,3}, {2,3,4}};
int[] arrAry2[] = new int[][]{{1,2,3}, {2,3,4}};
int arrARry3[][] = {{1,2,3}, {3,4,5}};
// 取值
System.out.println(arrAry[0][0]); //1
System.out.println(arrAry.length); //3
for(int i = 0; i < arrAry.length; i++) {
for(int j = 0; j < arrAry[i].length; j++) {
System.out.println((arrAry[i][j]));
}
}
}
}
不同类型数组的初始值
- 不同类型的值如果定义是外层是地址,内层和一维数组的初始值相同
int[][] arr1 = new int[3][2];
System.out.println(arr1[0]); // 地址值
System.out.println((arr1[0][0])); // 0
- 如果是初始化定义不同,如下,外层为空,内层会报错空指针
int[][] arr2 = new int[4][];
System.out.println(arr2[0]); // null 由于二维数组没有定义值,导致栈中没有存储地址值
System.out.println(arr2[0][0]); // 报错空指针,没有地址值再去取值会报空指针
存储结构
数组中常见算法
案例一
// 定义一个int类型一维数组,包含10个元素,分别赋值一些随机整数,然后求出元素最大值,最小值,总和,并输出
// 所有的随机整数都是两位数[10,99]
class TestCase1 {
public static void main(String[] args) {
int[] nums = new int[10];
int len = nums.length;
for(int i = 0; i < len; i++) {
nums[i] = (int)(Math.random()*90)+10;
}
int maxNum = nums[0];
int minNum = nums[0];
int sum = 0;
int average = 0;
for(int i = 0; i < len; i++) {
maxNum = nums[i] > maxNum ? nums[i] : maxNum;
minNum = nums[i] < minNum ? nums[i] : minNum;
sum += nums[i];
}
average = sum/len;
System.out.println(maxNum);
System.out.println(minNum);
System.out.println(sum);
System.out.println(average);
}
}
案例二
// 评委打分
// 编程竞赛中,10位评委打分 - 5.4.6.8.9.0.1.2.7.3
// 求最后得分 - (去掉一个最高分,去掉一个最低分后其余8位评委的平均分)
class TestCase2{
public static void main(String[] args) {
int[] scores = {5,4,6,8,9,0,1,2,7,3};
int maxNum = scores[0];
int minNum = scores[0];
double sumNum = 0.0;
for(int i = 0; i < scores.length; i++) {
maxNum = scores[i] > maxNum ? scores[i] : maxNum;
minNum = scores[i] < minNum ? scores[i] : minNum;
sumNum += scores[i];
}
sumNum = sumNum - (maxNum + minNum);
System.out.println(sumNum/8);
}
}
案例三 - 杨辉三角
// 利用二维数组 - 实现10行杨辉三角数据
// 1. 第一行有1个元素,第n行有n个元素
// 3. 从第三行开始,对于非第一个元素和最后一个元素的元素,等于数值相加
class TestCase3 {
public static void main(String[] args) {
// 初始化定义数组长度
int[][] arr = new int[10][];
for(int i = 0; i < arr.length; i++) {
// 定义每一行的长度 - 内层数组的个数
arr[i] = new int[i+1];
for(int j = 0; j < i+1; j++) {
if(j == 0 || (j > 0 && j == arr[i].length - 1)) {
// 每一行第一个元素和最后一个元素为1
arr[i][j] = 1;
}else {
// 其余数为上一行对应数据和对应数据前一个数据相加
arr[i][j] = arr[i-1][j-1] + arr[i-1][j];
}
}
}
// 打印二维数组
for(int i = 0; i < arr.length; i++) {
for(int j = 0; j < arr[i].length;j++) {
System.out.print(arr[i][j] + "\t");
}
System.out.print("\n");
}
}
}
案例四
// 生成一个随机数 - 随机数为[1,30],要求加入数组且数组长度为6,但数组中的随机数均不相等
class TestCase4 {
public static void main(String[] args) {
int[] arr = new int[6];
for(int i = 0; i < arr.length; i++) {
// 获取随机数
arr[i] = (int) (Math.random() * 30) + 1;
for (int j = 0; j < i; j++) {
if (arr[i] == arr[j]) {
i--;
break;
}
}
}
for(int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
案例五- 数组的拷贝
与前端相似,数组也有拷贝,他们的原理是相似的,可以看一下代码
class ArrayTest {
public static void main(String[] args) {
int[] array1 = {2,3,5,6,11,13,17,19};
int[] arrary2, array3;
for (int i = 0; i <array1.length; i++) {
System.out.println(array1[i]);
}
// 赋值操作 - 理解为前端的浅拷贝,只拷贝了地址值;
// 1和2都指向了同一个地址值,导致修改1,2中的任何一个值,另一个都会被修改
arrary2 = array1;
arrary2[0] = 0;
// 深拷贝,arrary3 的值和arrary1的数据相同,但地址值不同,修改互不影响
array3 = new int[array1.length];
for(int i = 0; i < array1.length; i++) {
array3[i] = array1[1]
}
System.out.println(array1[0]);
}
}
案例六 - 数组的反转
// 数组的翻转 - [35,54,3,2,65,7,34,5,76,34,5,76,34,67]
class TestCase45 {
public static void main(String[] args) {
int[] arr = {35,54,3,2,65,7,34,5,76,34,5,76,34,67};
for(int i = 0; i < arr.length/2; i++) {
int temp = arr[i];
arr[i] = arr[arr.length - 1 -i];
arr[arr.length - 1 -i] = temp;
}
int[] arr2 = {35,54,3,2,65,7,34,5,76,34,5,76,34,67};
for(int i = 0, j = arr2.length - 1; i < j; i++,j--) {
int temp = arr2[i];
arr2[i] = arr2[j];
arr2[j] = temp;
}
}
}
案例七- 数组的扩容与缩容
class TestCase6 {
public static void main(String[] args) {
// 数组的扩容
// 定义一个数组{1,2,3,4,5}, 将数组扩容1倍,并将,10,20,30添加到数组
int[] arr1 = {1,2,3,4,5};
// int[] arr2 = new int[arr1.length * 2];
int[] arr2 = new int[arr1.length << 1];
for(int i = 0; i < arr2.length; i++) {
arr2[i] = arr1[i];
}
arr2[arr1.length] = 10;
arr2[arr1.length + 1] = 20;
arr2[arr1.length + 2] = 30;
// 数组的缩容
// 数组{1,2,3,4,5} 删除数组中索引为4的元素;
// 方法1 不新建数组,在原有数组操作 - 推荐
int[] arr = {1,2,3,4,5};
for(int i = 4; i < arr.length - 1; i++) {
arr[i] = arr[i + 1];
}
// 最后一个元素设置为初始值
arr[arr.length-1] = 0;
// 方法2 新建数组
int[] arr3 = new int[arr.length - 1];
for(int i = 0; i < 4; i++) {
arr3[i] = arr[i];
}
for(int i = 4; i < arr.length -1; i++) {
arr3[i] = arr[i+1];
}
}
}
案例八 - 线性查找
// 线性查找
class LineFind {
public static void main(String[] args) {
int[] arr = new int[]{34,54,2,4,56,7,8,10};
int target = 34;
// 方式一定义flag
boolean find = false;
for(int i = 0; i < arr.length; i++) {
if(target == arr[i]) {
find = true;
System.out.println("找到"+ target + "在索引" + i);
}
}
if(!find) {
System.out.println("没有找到");
}
// 方式二
int i = 0;
for(; i < arr.length; i++) {
if(target == arr[i]) {
System.out.println("找到"+ target + "在索引" + i);
}
}
if(i == arr.length) {
System.out.println("没有找到");
}
}
}
案例九-二分查找
// 二分查找 - 必须是有序的数组
class TestCase7 {
public static void main(String[] args) {
int[] arr = {1,3,5,7,9,13,15,16,17,18,36,46,78,98};
int target = 5;
int minIndex = 0;
int maxIndex = arr.length - 1;
boolean flag= false;
// 数组索引小于最大索引
while (minIndex <= maxIndex) {
int mid = minIndex + ((maxIndex - minIndex)/2);
if(target == arr[mid]) {
flag = true;
System.out.println("找到"+target+"索引位置为"+mid);
break;
}else if(target > arr[mid]) {
minIndex++;
}else {
maxIndex--;
}
}
if(!flag) {
System.out.println("未找到");
}
}
}
线性查找
- 优点:算法简单
- 缺点:执行率低,时间复杂度0(n)
二分法查找
- 优点:执行效率高
- 缺点: 算法更难,且数组必须为顺序排序
排序算法
衡量排序算法的优势
时间复杂度: 分析关键字的比较次数和记录的移动次数
空间复杂度:分析算法中需要多少辅助内存
稳定性:若两个记录A和B的关键值相等,但排序后,AB的先后次序保持不变,则称这种排序算法最稳定
排序算法分类
内部排序: 整个排序不需要借助外部存储器(如磁盘),所有排序操作在内存中完成
外部排序:参与排序的数据非常多,数据量非常大,计算器无法在整改排序过程中放在内存中完成,必须借助于外部存储完成
常见的排序
冒泡排序
每次相临的数据进行排序
如: 第一个元素与第二个元素排序,大的往后换;第二个在于第三个排序,大的往后;第一个索引的元素比完一轮,在进行第二元素排序;
// 冒泡排序
class TestCase8 {
public static void main(String[] args) {
int[] arr = new int[] {34,54,3,2,65,7,34,5,75,43,67};
for(int j = 0; j < arr.length - 1; j++) {
for(int i = 0; i < arr.length - 1 - j;i++) {
if(arr[i] > arr[i+1]) {
int temp = arr[i];
arr[i] = arr[i+1];
arr[i+1] = temp;
}
}
}
}
}
快速排序
第一轮:
以索引为0的值开始进行
public class QuickSort {
// 主方法,用于测试快速排序
public static void main(String[] args) {
int[] array = {10, 7, 8, 9, 1, 5};
int n = array.length;
QuickSort ob = new QuickSort();
ob.sort(array, 0, n - 1);
System.out.println("排序后的数组:");
printArray(array);
}
// 快速排序的递归实现
void sort(int[] array, int low, int high) {
if (low < high) {
// pi 是分区索引,array[pi] 已经在正确的位置
int pi = partition(array, low, high);
// 分别对左右子数组进行排序
sort(array, low, pi - 1);
sort(array, pi + 1, high);
}
}
// 分区操作,返回分区索引
int partition(int[] array, int low, int high) {
int pivot = array[high]; // 选择最右边的元素作为基准
int i = (low - 1); // i 是较小元素的索引
for (int j = low; j < high; j++) {
// 如果当前元素小于或等于基准
if (array[j] <= pivot) {
i++;
// 交换 array[i] 和 array[j]
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
// 交换 array[i + 1] 和 array[high] (或基准)
int temp = array[i + 1];
array[i + 1] = array[high];
array[high] = temp;
return i + 1;
}
// 打印数组的方法
static void printArray(int[] array) {
int n = array.length;
for (int i = 0; i < n; ++i)
System.out.print(array[i] + " ");
System.out.println();
}
}
数组的工具类 - java.util.Arrays
java.util.Arrays类即为操作数组的工具类,包含了用来操作数组的各种方法
class testUtilArray {
public static void main(String[] args) {
int[] arr = new int[]{1,4,5,6,7};
int[] arr2 = new int[]{1,8,4,7,5};
// 对比arr和arr2的元素是否相等
System.out.println(Arrays.equals(arr, arr2));
// string,输出
Arrays.toString(arr);
// 将制定数值填充
Arrays.fill(arr, 1);
// sort排序
Arrays.sort(arr2);
//二分查找 - 保证是排序了
int num = Arrays.binarySearch(arr, 1); // 负数为没有找到
}
}
异常
角标:ArrayIndexOutOfBoundException
空指针异常:NullPointerException
class TestError {
public static void main(String[] args) {
// ArrayIndexOutOfBoundException
int[] arr = {1,2,3,4};
System.out.println(arr[5]);
//NullPointerException
int[] arr1 = new int[10];
arr1 = null;
System.out.println(arr1[0]);
int[][] arr2 = new int[3][];
System.out.println(arr2[0][1]);
String[] arr3 = new String[4];
System.out.println(arr3[0]);
}
}