java数组
java数组
数组是用于存储多个相同类型数据的存储模型,是相同数据类型的有序集合。数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成。其中,每一个数据称作一个元素,每个元素可以通过一个索引(下标)来访问它们。
为什么使用数组
如何存储100名学生的成绩?
- 办法:使用变量存储,重复声明100个double类型变量即可。
- 缺点:麻烦,重复操作过多。
如何让100名学生成绩全部+1?
- 办法:100个变量重复相同操作,直至全部完毕。
- 缺点:无法进行统一的操作。
数组的创建方式
数组的定义格式:
(1)数据类型[] 数组名;
(2)数据类型 数组名[];
数组名相当于一个引用类型的变量名,它在栈内存中存放的是地址值,这个地址值指向堆内存
举例:
int[] a; 定义一个int类型的数组a变量
int a[]; 定义一个int类型的a数组变量
注意:这两种定义的效果可以认为是一样的,都是定义一个int类型的数组,但在念法上有些小区别,推荐使用第一种。
数组定义后,还需要给数组的元素赋值,没有元素值的数组是无法使用的。
数组的初始化
概述:数组的初始化,就是为数组中的元素分配内存空间,并为每一个数组元素赋值。
数组分为动态初始化、静态初始化
1、数组动态初始化
指定数组长度,由系统自行分配初始值之后再赋值,即先分配空间,再进行赋值。
格式:数据类型[] 变量名 = new 数据类型[ 数组长度 ] ;
数组长度其实就是数组中元素的个数
2、数组静态初始化
指定每个数组元素的初始值,由系统决定数组长度
格式:数据类型[] 变量名 = new 数据类型[] {数据1,数据2……};
简化格式: 数据类型[] 变量名 = {数据1,数据2……};
//动态初始化
int a = new int[3];//先分配空间
a[0] = 1;
a[1] = 2;
a[2] = 3;
//静态初始化
int[] b = {1,2,3}//声明的同时直接赋值
M[] m = {new M(),new M()}
/*
访问数组元素值的格式:
数组名[索引];
注意:索引(下标)其实就是数组中每个元素的编号,它从0开始,最大索引是数组长度-1。
注意:数组的动态初始化和静态初始化不能同时进行,只能任选其一。
*/
数组常见操作
1)数组遍历
依次输出数组中的每一个元素
public static void main(String[] args) {
int[] arr = { 1, 2, 3, 4, 5 };
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
注意:数组提供了一个length的属性,它用于获取数组的长度,其格式如下:
数组名.length
获取最值
获取数组中的最大值或最小值
/*
实现思路:
1. 定义max变量,保存数组0索引上的元素
2. 遍历数组,获取数组中的每个元素
3. 将遍历到的元素逐一和max变量进行比较
4. 如果数组元素的值大于max变量的值,将该值保存到max变量
5. 数组循环遍历结束,max变量保存的就是数组中的最大值
*/
public static void main(String[] args) {
int[] arr = { 5, 15, 2000, 10000, 100, 4000 };
//定义变量,保存数组中0索引的元素
int max = arr[0];
//遍历数组,取出每个元素
for (int i = 0; i < arr.length; i++) {
//遍历到的元素和变量max比较
//如果数组元素大于max
if (arr[i] > max) {
//max记录住大值
max = arr[i];
}
}
System.out.println("数组最大值是: " + max);
}
(3)元素反转
就是把元素前后对调
/*
实现思想:数组最远端的元素互换位置。
1. 实现反转,就需要将数组最远端元素位置交换
2. 定义两个变量,保存数组的最小索引和最大索引
3. 两个索引上的元素交换位置
4. 最小索引++,最大索引--,再次交换位置
5. 最小索引超过了最大索引,数组反转操作结束
*/
public static void main(String[] args) {
int[] arr = { 1, 2, 3, 4, 5 };
/*
循环中定义变量min=0最小索引
max=arr.length‐1最大索引
min++,max‐‐
*/
for (int min = 0, max = arr.length ‐ 1; min <= max; min++, max‐‐) {
//利用第三方变量完成数组中的元素交换
int temp = arr[min];
arr[min] = arr[max];
arr[max] = temp;
}
// 反转后,遍历数组
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
(4)查表法
根据键盘录入索引,查找对应元素
(5)数组排序
选择排序
使用下标标记较大或较小的数,都比较完之后再交换两个数
int[] array = {2,5,1,3,6,4,8,9,7};
int count = 0;
for (int i = 0; i < array.length; i++) {
int index = i;
for (int j = i; j < array.length; j++) {
if (array[index] > array[j]) {
index = j;
}
}
if (index != i) { //找到了比array[i]小的则与array[i]交换位置
int t = array[i];
array[i] = array[index];
array[index] = t;
count++;
}
System.out.print(array.length - 1 > i ? array[i] + ", " : array[i]);
}
System.out.println("\n交换次数:" + count); // 交换7次
冒泡排序
比较两个数的大小,交换两个数
int[] array = {2,5,1,3,6,4,8,9,7};
int count = 0;
for (int i = 0; i < array.length; i++) {
for (int j = i; j < array.length; j++) {
if (array[i] > array[j]) {
int t = array[i];
array[i] = array[j];
array[j] = t;
count++;
}
}
System.out.print(array.length - 1 > i ? array[i] + ", " : array[i]);
}
System.out.println("\n交换次数:" + count); // 交换7次
使用Arrays.sort()排序
- 对基本数据类型排序使用优化的快速排序算法
- 对引用类型排序使用优化的合并排序算法
public static void main(String[] args){
int[] nums = new int[]{4,3,5,2,1};
//借助JDK提供的数组工具,进行排序
Arrays.sort(nums);
//第一次遍历(升序)
for(int i = 0 ; i < nums.length ; i++){
System.out.println(nums[i]);
}
//降序:需要手工的方式完成元素的倒置 5 2 3 4 1
for(int i = 0 ; i < nums.length / 2 ; i++){// i = 0
int temp = nums[i];// int temp = 1;
nums[i] = nums[ nums.length - 1 - i];
nums[ nums.length - 1 - i] = temp;
}
//第二次遍历(降序)
for(int i = 0 ; i < nums.length ; i++){
System.out.println(nums[i]);
}
//两值交换,借助第三变量
/*
int a = 10;
int b = 20;
int c = a;//将a中的值保存在c中
a = b;//将b中的值保存在a中
b = c;//将c中的值保存在b中
*/
}
(6)查找
普通查找
二分查找
(折半检索):先把数组折半,再进行比较,大于中间数则用中间值加一除以二再比较,直至两数相等 注意:要查找的数组必须是有序的,否则查找结果不准确
static int binaryS(int a[], int key) {
int low = 0;
int high = a.length - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (a[mid] > key)
high = mid - 1;
else if (a[mid] < key)
low = mid + 1;
else
return mid;
}
return -1;
}
(7)数组作为方法参数
数组作为方法参数传递,传递的参数是数组内存的地址。
public static void main(String[] args) {
int[] arr = { 1, 3, 5, 7, 9 };
//调用方法,传递数组
printArray(arr);
}
/*
创建方法,方法接收数组类型的参数
进行数组的遍历
*/
public static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
(8)数组作为方法返回值
数组作为方法的返回值,返回的是数组的内存地址
public static void main(String[] args) {
//调用方法,接收数组的返回值
//接收到的是数组的内存地址
int[] arr = getArray();
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
/*
创建方法,返回值是数组类型
return返回数组的地址
*/
public static int[] getArray() {
int[] arr = { 1, 3, 5, 7, 9 };
//返回数组的地址,返回到调用者
return arr;
}
(9)数组越界异常
原因:访问了数组中不存在的索引时发生
public static void main(String[] args) {
/*
创建数组,赋值3个元素,数组的索引就是0,1,2,没有3索引,因此我们不能访问数组中不存在的索引,程序运
行后,将会抛出 ArrayIndexOutOfBoundsException 数组越界异常。
*/
int[] arr = {1,2,3};
System.out.println(arr[3]);
}
(10)数组空指针异常
原因:数组引用没有指向实体,却在操作实体中的元素时发生
public static void main(String[] args) {
/*
arr = null 这行代码,意味着变量arr将不会在保存数组的内存地址,也就不允许再操作数组了,因此运行的时候
会抛出 NullPointerException 空指针异常。
*/
int[] arr = {1,2,3};
arr = null;
System.out.println(arr[0]);
}
数组扩容
数组扩容:本质上就是定义一个新的数组,把原数组的数据复制到新数组,后面填充null
创建数组时,必须显示指定长度,并在创建之后不可更改长度。
扩容的思路:
- 创建大于原数组长度的新数组。
- 将原数组中的元素依次复制到新数组中。
- 将新数组的地址赋值给原数组
数组复制的方式
- for循环
public static void main(String[] args){
int[] nums = new int[5];//数组创建之后,长度不可变
nums[0] = 11;
nums[1] = 22;
nums[2] = 33;
nums[3] = 44;
nums[4] = 55;
//1.创建比原数组大的新数组
int[] newNums = new int[ nums.length * 2 ];
//2.复制原数组中的所有数据到新数组
for(int i = 0 ; i < nums.length ; i++){
newNums[i] = nums[i];
}
//遍历原数组
for(int i = 0 ; i < nums.length ; i++){
System.out.print( nums[i] +"\t");
}
System.out.println();
//遍历新数组
for(int i = 0 ; i < newNums.length ; i++){
System.out.print( newNums[i] +"\t");
}
}
- 使用System.arraycopy()
System.arraycopy(原数组,原数组起始,新数组,新数组起始,长度)
public static void main(String[] args){
int[] nums = new int[5];//数组创建之后,长度不可变
nums[0] = 11;
nums[1] = 22;
nums[2] = 33;
nums[3] = 44;
nums[4] = 55;
//复制的赋值
//1.创建新数组
int[] newNums = new int[ nums.length * 2 ];
//2.使用System.arraycopy(原数组,原数组起始下标,新数组,新数组的起始存储下标,需要赋值的个数或长度);
System.arraycopy( nums , 0 , newNums , 0 , nums.length);
//遍历新数组
for(int i = 0 ; i < newNums.length ; i++){
System.out.print( newNums[i] +"\t");
}
}
- 使用Arrays.copyof()
Arrays.copyof(要复制的数组,复制长度),返回带有原值的新数组
public static void main(String[] args){
int[] nums = new int[5];//数组创建之后,长度不可变
nums[0] = 11;
nums[1] = 22;
nums[2] = 33;
nums[3] = 44;
nums[4] = 55;
//1.创建新数组、复制元素
int[] newNums = java.util.Arrays.copyOf(nums , nums.length * 2);//将带有原值的新数组返回给我们
//2.遍历
for(int i = 0 ; i < newNums.length ; i++){
System.out.print( newNums[i] +"\t");
}
}
地址替换
- 数组作为引用类型之一,其变量中存储的是数组的地址。
- 完成元素复制后,需将新数组地址,赋值给原变量进行替换。
public static void main(String[] args){
int[] nums = new int[5];//数组创建之后,长度不可变
nums[0] = 11;
nums[1] = 22;
nums[2] = 33;
nums[3] = 44;
nums[4] = 55;
//复制的赋值
//1.创建新数组
int[] newNums = new int[ nums.length * 2 ];
//2.使用System.arraycopy(原数组,原数组起始下标,新数组,新数组的起始存储下标,需要赋值的个数或长度);
System.arraycopy( nums , 0 , newNums , 0 , nums.length);
//遍历新数组
for(int i = 0 ; i < newNums.length ; i++){
System.out.print( newNums[i] +"\t");
}
//将新数组地址赋值给原数组,实现数组扩容
nums = newNums;
}
Arrays.copyof()与System.arraycopy()的区别:
- 写法区别:
- Arrays.copyof(要复制的数组,复制长度),底层由System.arraycopy()实现
- System.arraycopy(要复制的数组,起始位置,复制到哪个数组,起始位置,复制长度)
- 数组个数:
- Arrays.copyof()只要调用该方法就会创建新的数组,无需手动创建
- System.arraycopy()不会创建新的数组,可以复制到原数组上,有需要时,要先创建一个新数组
数组内存图
一个数组内存图
public static void main(String[] args) {
int[] arr = new int[3];
System.out.println(arr);//[I@5f150435
}
两个数组内存图
public static void main(String[] args) {
int[] arr = new int[3];
int[] arr2 = new int[2];
System.out.println(arr);
System.out.println(arr2);
}
两个变量指向一个数组
public static void main(String[] args) {
// 定义数组,存储3个元素
int[] arr = new int[3];
//数组索引进行赋值
arr[0] = 5;
arr[1] = 6;
arr[2] = 7;
//输出3个索引上的元素值
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
//定义数组变量arr2,将arr的地址赋值给arr2
int[] arr2 = arr;
arr2[1] = 9;
System.out.println(arr[1]);
}
数组的三个基本特点
- 长度是确定的。数组一旦被创建,它的大小就是不可以改变的。
- 其元素必须是相同类型,不允许出现混合类型。
- 数组类型可以是任何数据类型,包括基本类型和引用类型。
数组变量属于引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量。
稀疏数组
只记录原始数组有几行几列、有多少个有效值
当一个数组中大部分都是0或者同一个值的时候,可以用稀疏数组来保存
二维数组
概述:二维数组其实就是一个里面存储的元素为一维数组的数组。即数组中存储的元素还是数组
声明方式
(1)数组类型[][] 数组名 = new 数组类型[m][n];
// 声明方式与一维数组相似
数组类型[][] 数组名 = new 数组类型[m][n];
// m表示这个二维数组有多少个一维数组,n表示每个一维数组的元素个数
int[][] i = new int[3][2];
// 创建了一个数组,长度为3,每个位置存储了一个数组,每个位置存储的数组长度都是2
// 即:总共创建了四个数组,一个数组里面存储了三个长度为2的数组,注意:这里所说的都是数组的地址,即存储的是子数组的地址
以下格式也可以定义一个二维数组,但不推荐:
数据类型 数组名[][] = new 数据类型[m][n];
数据类型[] 数组名[] = new 数据类型[m][n];
(2)数据类型[][] 数组名 = new 数据类型[m][];
// 只定义外围数组的长度,每个位置的一维数组的长度未知
数据类型[][] 数组名 = new 数据类型[m][];
// 外围数组第一个位置存储了一个长度为n1的一维数组
数组名[0] = new 数据类型[n1];
// 外围数组第二个位置存储了一个长度为n2的一维数组
数组名[1] = new 数据类型[n2];
// 注意:n1和n2可以不相等
...
数组名[m-1] = new 数据类型[nm];
说明:
m表示这个二维数组有多少个一维数组
第二种格式没有直接给出一维数组的元素个数,这时可以动态地给一维数组分配内存空间
(3)数据类型[][] 数组名 = new 数据类型[][]{{元素1,元素2…},{元素1,元素2…},{元素1,元素2…}…};
数据类型[][] 数组名 = new 数据类型[][]{{元素1,元素2...},{元素1,元素2...},{元素1,元素2...}...};
简化格式:
数据类型[][] 数组名 = {{元素1,元素2...},{元素1,元素2...},{元素1,元素2...}...};
访问二维数组元素值的格式:
数组名[m][n];
说明:表示访问第m+1个一维数组的第n+1个元素
二维数组的内存分配
高维数组中的每一个元素,保存了低维数组的地址。访问array[0]等价于在访问0x0000A111。
二维数组的操作
(1)二维数组遍历:外循环控制二维数组的长度,也就是一维数组的个数;内循环控制一维数组的长度。
public static void main(String[] args){
int[][] nums = new int[3][5];
nums[0][0] = 10;//第一行,第一列
nums[0][3] = 20;//第一行,第四列
nums[1][0] = 30;//第二行,第一列
nums[1][1] = 40;//第二行,第二列
nums[2][2] = 50;//第三行,第三列
nums[2][4] = 60;//第三行,第五列
for(int i = 0 ; i < nums.length ; i++){ //外层控制行数
for(int j = 0 ; j < nums[i].length ; j++){ // j = 1 内层控制列数
System.out.print( nums[i][j] +"\t");
}
System.out.println();
}
System.out.println( nums.length );//nums变量中,所持有的地址对应的数组长度是多少 = 3
System.out.println( nums[0].length );// nums[0],高位数组中的第一个元素所指向的低维数组的长度
System.out.println( nums[0] ); // 长度5 先创建了长度为5的低维数组,将地址赋值给高维空间
System.out.println( nums[1] );
System.out.println( nums[2] );
}
(2)二维数组求和
(3)打印杨辉三角
public static void main(String[] args){
//####1#
//###1#1
//##1#0#1
//#1#0#0#1
//1#0#0#0#1
int rows = 7;
int[][] yh = new int[rows][];
//创建多个不同长度的二维数组
for(int i = 0 ; i < rows ; i++){
yh[i] = new int[i+1];
}
//完成初始值的赋值(每行的首位都是1)
for(int i = 0 ; i < yh.length ; i++){ // i = 2
yh[i][0] = 1;
yh[i][i] = 1;
}
//计算
for(int i = 2 ; i < yh.length ; i++){
for(int j = 1 ; j < i ; j++){
//当前位置的值 = 上一行的同列 + 上一行的前一个列
yh[i][j] = yh[i-1][j] + yh[i-1][j-1];
}
}
for(int i = 0 ; i < yh.length ; i++){
for(int j = rows - 1 ; j > i ; j--){//满足4次
System.out.print("\t");
}
for(int j = 0 ; j < yh[i].length ; j++){
System.out.print( "\t"+ yh[i][j] +"\t");
}
System.out.println();
}
}