第6章 数组
文章目录
引言
单个的数组变量可以引用一个大的数据集合,为了解决需要存储大量数据,需要一个高效有条理的方法,Java和许多高级语言都提高了一种称作数组的数据结构,可以用它来存储一个元素个数固定且元素类型相同的有序集。
一、数组的概念及定义
数组主要用于解决大量数据计算与存储的问题
比如:输入100个数字,统计其中的最大值和最小值并计算平均值,创建100个变量,会有一堆if- else语句,比较麻烦。
数组是Java提供的一种最简单的数据结构,可以用来存储一个元素 个数固定 且 类型相同 的有序
集。
1.数组在内存中的情况
栈:主要用于运行函数的内存
堆:主要用于存储数据对象的内存
每一个数组而言,都是存在堆内存当中,每一个数组都是一个对象
数组本质上就是在堆内存中一系列地址连续且空间大小相等的存储空间(变量),每一个存储空间用来存储数据(基本,引用)
数组是在堆内存中存储,称之为是一个数组对象,并且在堆内存中存储的数据都有 默认初始化的流程。所以数组创建之初,每一个存储空间里面都会被JVM初始化该数据类型对应的零值。
数组的地址是连续的,所以通过公式:An=A1+(n-1)*d可以快速访问到其他的元素,所以对于数组而言查找元素比较快的。将元素的真实物理地址转换成对应的角标获取元素。
如何来调用数组呢?通过一个变量存储该数组在堆内存当中的首元素的地址。
当数组一旦定义出来,其长度不可变,存储空间的内容是可变的
所以我们在定义数组的时候,要么把长度固定,要么直接输入相关的元素。
2.数组的定义方式
//创建一个指定长度且指定数据类型的一维数组,名称为数组名,虽然没有指定元素,但是会有默认值
数据类型[] 数组名 = new 数据类型[长度];
//创建一个指定元素且指定数据类型的一维数组,名称为数组名,虽然有指定元素,还是有默认初始化这个步骤的!
数据类型[] 数组名 = new 数据类型[]{数据1,数据2,...,数据n};
数据类型[] 数组名 = {数据1,数据2,...,数据n};
public class Sample {
public static void main(String[] args) {
int[] arr = new int[5];
System.out.println(arr[0]);
//System.out.println(arr[5]);
//ArrayIndexOutOfBoundsException arr[2] = 10;
int[] arr2 = arr;
System.out.println(arr2[2]);
arr2 = null;
//System.out.println(arr2[2]);
//NullPointerException
/*
String s = null;
s.length();
*/
arr = null;
}
}
二、常用数组操作
1.数组遍历问题
public class Sample01 {
public static void main(String[] args) {
//1.声明一个数组
int[] arr = new int[] {1,2,3,4,5,6,7,8,9};
//String str str.length()-函数
//int[] arr arr.length-属性
for (int i = 0; i < arr.length; i++) {
arr[i] = arr[i] * 10;
System.out.println(arr[i]);
}
//通过角标遍历 可以在遍历的过程中对指定的元素进行修改
//foreach遍历 主要针对的是一些可迭代对象 Iterable
/*
for (数据类型 变量名 : 可迭代容器) {
}
*/
for (int num : arr) {
//num -> arr[i]
num = num / 10;
System.out.println(num);
}
//这种遍历方式 只能获取元素,不能修改元素
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
这里扩充说明一下foreach的用法:
在java中foreach是for循环的增强版,它可以减少代码量,可以代替复杂的for循环,但是并不是所有的都可以代替,主要针对的是一些可迭代对象 Iterable。
所以foreach 语法结构提供了遍历数组的简单方式。foreach 仅能够应用于数组和对象,用于其他数据类型会报语法错误。
for(元素类型type 元素变量value :遍历对象obj){
引用x的java语句
}
例如:
public class Demo90 {
public static void main(String[] args) {
int[] arr = new int[] {1,2,3,4,5,6,7,8,9};
for (int num : arr) {
//num -> arr[i]
System.out.println(num);
}
}
}
结果为:
2.数组最值问题
public class Sample02 {
public static void main(String[] args) {
//1.声明一个数组,定义最小值和最大值都为数组里面第一个
int[] arr = new int[] {3,5,4,6,8,7,1,2,9};
int min = arr[0];
int max = arr[0];
//2. 通过循环将数组的第一个与后门进行比较,重新对最大值和最小值进行赋值
for (int i = 1; i < arr.length; i++) {
if (arr[i] < min) {
min = arr[i];
}
if (arr[i] > max) {
max = arr[i];
}
}
System.out.println(min);
System.out.println(max);
}
}
3.数组扩容问题
public class Sample03 {
public static void main(String[] args) {
//1.声明一个数组
int[] arr = new int[]{1,2,3,4,5};
arr = add(arr,6);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
//在指定的数组arr中添加元素element
public static int[] add(int[] arr, int element) {
int[] newArr = new int[arr.length + 1];
for (int i = 0; i < arr.length; i++) {
newArr[i] = arr[i];
}
newArr[newArr.length - 1] = element;
return newArr;
}
}
4.可变长参数列表
public class Sample {
public static void main(String[] args) {
show(1);
show(1,2,3);
show("hehe","lala","haha","xixi","heihei");
}
public static void show(int ... nums) {
//省略号的长度是由上面show里面数字或者字符串有多少定义的,可随意变动
for (int i = 0; i < nums.length; i++) {
System.out.print(nums[i] + " ");
}
System.out.println();
}
public static void show(String ... strs) {
//省略号的长度是由上面show里面数字或者字符串有多少定义的,可随意变动
for (int i = 0; i < strs.length; i++) {
System.out.print(strs[i] + " ");
}
System.out.println();
}
}
5.二分查找算法
有一个前提,所查找的数据集必须是有序的(升序,降序)
图分解:
public class Sample07 {
public static void main(String[] args) {
int [] arr = new int[]{1,2,3,4,5,6,7,8,9};
//定义角标的初始化值
int min = 0;
int max = arr.length - 1;
int mid = (min + max) / 2;
int key = 4;
while (arr[mid] != key) {
if (key < arr[mid]) {
max = mid - 1;
}
if (arr[mid] < key) {
min = mid + 1;
}
if (min > max) {
mid = -1;
break;
}
mid = (min + max) / 2;
}
System.out.println(mid);
}
}
6.二分查找时间复杂度
二分查找的时间复杂程度是O(logN)
我们假设最坏的情况,要查找的数字在最后,n为数组长度,二分查找每次分一半,利用公式找的时间复杂度为O(logn),看坐标图可以看书二分查找的速度很快。(下图还举例了三分查找,速度会更快)
分析图:
还有一些排序的算法也有用到了一维数组,就如选择排序,冒泡排序,计数排序和基数排序等等,这些方法会在总结的十大排序算法的文章里进行图文详解。
三、二维数组
1.什么是二维数组
二维数组,在表现形式上就是一个表格,在操作表格的时候以行列来操作。
所谓的二维数组,本质上就是一个一维数组,只不过该一维数组里面的元素是另一个一维数组而已
问:一个4×5的二位数组,一共有几个一维数组组成?
5个:最外层一个+四个子数组
四、二维数组的定义
//数据类型[][] 矩阵名 = new 数据类型[row][col];
int[][] matrix = new int[3][2];
/*
数据类型[][] 矩阵名 = new 数据类型[][] {
{...},
{...},
{...}
};
数据类型[][] 矩阵名 = {
{...},
{...},
{...}
};
*/
int[][] matrix2 = {
{1,2,3},
{4,5,6},
{7,8,9}
};
举例代码:
public class Sample {
public static void main(String[] args) {
//数据类型[][] 矩阵名 = new 数据类型[row][col];
int[][] matrix = new int[3][2];
/*
数据类型[][] 矩阵名 = new 数据类型[][] {
{...},
{...},
{...}
};
数据类型[][] 矩阵名 = {
{...},
{...},
{...}
};
*/
int[][] matrix2 = { {1,2,3}, {4,5,6}, {7,8,9} };
for (int i = 0; i < matrix2.length; i++) {
for (int j = 0; j < matrix2[i].length; j++) {
System.out.print(matrix2[i][j] + " ");
}
System.out.println();
}
int[][] matrix3 = {
{1},
{1,2,3},
{1,2,3,4},
{7,6,5,4,3,2,1}
};
for (int i = 0; i < matrix3.length; i++) {
for (int j = 0; j < matrix3[i].length; j++) {
System.out.print(matrix3[i][j] + " ");
}
System.out.println();
}
}
}
运行结果:
eg、输入8个点坐标,然后计算这些点中,那两个点的距离是最近的
思路:运用了选择排序,比较两点大小,找到最小距离,只是不交换位置
import java.util.Scanner;
public class Sample {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
double[][] points = new double[8][2];
//1.获取输入的点坐标
for (int i = 0; i < points.length; i++) {
System.out.print("请输入第" + (i + 1) + "个点坐标:");
points[i][0] = input.nextDouble();
points[i][1] = input.nextDouble();
}
double shortestDistance = getDistance(points,0,1);
int p1 = 0;
int p2 = 1;
for (int i = 0; i < points.length - 1; i++) {
for (int j = i + 1; j < points.length; j++) {
double distance = getDistance(points,i,j);
if (distance < shortestDistance) {
shortestDistance = distance;
p1 = i;
p2 = j;
}
}
}
System.out.printf("(%.1f,%.1f)和(%.1f,%.1f)的距离为最短%.1f",points[p1] [0],points[p1][1],points[p2][0],points[p2][1],shortestDistance);
}
public static double getDistance(double[][] m , int p1 , int p2) {
return Math.hypot(m[p1][0] - m[p2][0] , m[p1][1] - m[p2][1]);
}
}
运行结果: