数组
数组概念
定义
- 数组是相同类型数据的有序集合
- 数组描述的是相同类型的若干个数据,按照一定先后次序进行排列组合而成
- 每一个数据被称为一个数组元素,每个数组元素可以通过下标访问他们
数组的声明与创建
首先必须声明数组变量,才能在程序中使用数组
语法:
dataType[] arrayRefVar; //首选的方法
dataType arrayRefVar[]; //效果相同,但是不是首选的方法
java使用new操作符来创建一个数组
dataType arrayRefVar = new dataType[arraySize]
数组元素都是通过索引访问的,数组索引从0开始
获取数组长度: arrays.length
让我们举个栗子
public class ArrayDemo01 {
//变量的类型 变量的名字 = 变量的值;
//数组类型
public static void main(String[] args) {
int[] numbers1; //声明一个数组
// int numbers2[]; //声明一个数组的第二种方式
numbers1 = new int[10]; // 创建一个数组,这里面可以存放10个int类型的数字
//对其赋值,如果未对其赋值,则int类型为0,string等则为null(空)
numbers1[0] = 1;
numbers1[1] = 2;
numbers1[2] = 3;
numbers1[3] = 4;
System.out.println(numbers1[2]);
System.out.println(numbers1[5]);
System.out.println("================");
//计算和
int sum = 0;
for (int i = 0; i < numbers1.length ; i++) {
sum = sum + numbers1[i];
}
System.out.println("该数组和为:"+sum);
}
}
内存分析
存放new的对象和数组
堆
可以被所有线程共享,不会存放别的对象引用
存放基本变量类型(会包含这个基本类型的具体数值)
java内存 栈
引用对象的变量(会存放这个引用在堆里面的具体数值)
可以被所有线程共享
方法区
包含了所有class和static变量
三种初始化
静态初始化
int[] a = {1,2,3};
Man[] mans = {new Man(1,1),new Man(2,2)}
动态初始化
int[] a = new int[2];
a[0] = 1;
a[1] = 2;
数组的默认初始化
数组是引用类型,他的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也按照实例变量同样的方式被隐形初始化
举个栗子
public class ArrayDemo02 {
public static void main(String[] args) {
//静态初始化 创建 + 赋值
int[] a = {1,2,3,4,5,6};
System.out.println(a[3]);
//动态初始化
int[] b = new int[5];
b[0] = 10;
System.out.println(b[0]); //已经被定义,输出10
System.out.println(b[1]); //未被定义,输出0
}
}
数组的四个基本特点
- 其长度是确定的,数组一旦被创建,它的大小就不可改变
- 其元素必须是相同类型,不允许出现混合类型
- 数组元素可以是任意数据类型,包括基本类型和引用类型
- 数组变量属于引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量,数组本身就是对象,Java中对象是在堆中,因此数组无论保存原始类型还是其他对象类型数组对象本身是在堆中
数组边界
下标合法区间:[0,length-1],如果越界,则会报错
public static void main(String[] args){
int[] a = new int[2];
System.out.println(a[2])
}
上述代码中数组长度只有2,但是这里让其显示第三个,则会数组越界
报错:ArrayIndexOutOfBoundsException //数组下标越界异常
小结:
- 数组是相同数据类型的集合(数据类型可以是类型)的有序结合
- 数组也是对象,数据元素相当于对象的成员变量
- 数组长度是确定的,不可变的,越界则报错 ArrayIndexOutOfBounds
数组的使用
- 普通的For循环
- For--Each循环
- 数组做方法入参
- 数组做返回值
public class ArrayDemo03 {
public static void main(String[] args) {
int[] a = {1,2,3,4,5,6};
//打印全部数组元素
for (int i = 0; i < a.length; i++) {
System.out.println(a[i]);
}
System.out.println("============");
//计算所有元素的和
int sum = 0;
for (int i = 0; i < a.length; i++) {
sum = sum + a[i];
}
System.out.println("和为:"+sum);
System.out.println("==============");
//查找最大元素
int max = a[0];
for (int i = 1; i < a.length ; i++) {
if (a[i] > max ){
max = a[i] ;
}
}
System.out.println("最大数为:"+max);
}
}
public class ArrayDemo04 {
public static void main(String[] args) {
int[] arrays = {1, 2, 3, 4, 5};
// //For-Each循环
// //增强型for循环,没有下标
// for (int i : a) {
// System.out.println(i); //直接打印输出所有数组元素
// }
printArray(arrays); //打印输出arrays中数组
System.out.println(" ");
int[] reverse = reverse(arrays);
printArray(reverse); //打印输出reverse被反转的数组
}
//打印数组元素
public static void printArray(int[] arrays) {
for (int i = 0; i < arrays.length; i++) {
System.out.print(arrays[i]+" ");
//打印输出1 2 3 4 5
}
}
//反转数组
public static int[] reverse(int[] arrays){
int[] result = new int[arrays.length];
//进行反转操作
for (int i = 0, j = result.length-1; i < arrays.length ; i++,j--) {
result[j] = arrays[i];
}
return result;
}
}
我们用for循环往char数组中填入值
package javaSEStudy.basics.Studay.Array;
public class ArrayDemo10 {
public static void main(String[] args) {
//创建一个char类型的26个元素的数组,分别放置'A'到'Z'
//使用for循环访问所有元素并打印
//char类型运算'A'+1 -> 'B'
char[] chars = new char[26];
for (int i = 0; i < chars.length; i++) {
//'A'+i 是int类型,chars[i]是char类型,需要强制类型转换
//chars是char[]
chars[i] = (char) ('A' + i);
}
//循环输出
for (int i = 0; i < chars.length; i++) {
System.out.print(chars[i]+" ");
}
}
}
多维数组
定义:多维数组可以看成数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组
二维数组:
int[][] a = new int[5][9];
解析:上述二维数组可以看成一个五行九列的数组
让我们举个栗子
public class ArrayDemo05 {
public static void main(String[] args) {
/*
1,2,1 array[0]
2,5,6 array[1]
9,5,6,4 array[2]
52,6,8 array[3]
*/
int[][] array = {{1,2,1},{2,5,6},{9,5,6,4},{52,6,8}};
System.out.println(array[0]); //这样输出是array[0]的对象标明,我这里输出的是[I@16b98e56
printArray(array[1]); //这样才能正确打印输出数组arrays中第二个数组的值
System.out.println(" ");
System.out.println(array[0][1]); //这样才输出了array数组地一个数组,第二个数的值
System.out.println(array[2][3]); //输出array数组第三个数组,第四个数的值
System.out.println("========");
//获取多维数组长度
System.out.println(array.length); //输出4,整个数组最外面有4个数组,输出4
System.out.println(array[0].length); //输出3,array第一个数组中有3个数
System.out.println("==========");
//该循环的遍历写法
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
System.out.println(array[i][j]); //输出了数组里所有的数
}
}
}
public static void printArray(int[] arrays) {
for (int i = 0; i < arrays.length; i++) {
System.out.print(arrays[i]+" ");
//打印输出数组的值
}
}
}
数组扩展
Arrays类
- 数组的工具类:java.util.Arrays
- 由于数组对象并没有什么方法给我们调用,但是api中提供了一个工具类给我们使用,从而可以对数据对象执行一些基本操作
- 查看JDK帮助文档
- Arrays类中的方法都是用static修饰的静态方法,在使用的时候可以直接使用类名去进行调用,而"不用"使用对象来调用(是"不用"而不是"不能")
具有以下常用功能
- 给数组赋值:通过fill方法
- 对数组排序:通过sort方法,升序
- 比较数组:通过equals方法比较数组元素中的元素值是否相等
- 查找数组元素:通过binarySearch方法能对排序好的数组进行二分查找法操作
让我们举个栗子
package Studay.Array;
import java.util.Arrays; //点进Arrays类,Crtl+鼠标左键
public class ArrayDemo06 {
public static void main(String[] args) {
int[] a = {1,5,6,45,59,622,46,462,159,66};
System.out.println(a); // 该输出为[I@16b98e56,这个数组在java存储的对象名
//打印数组元素Arrays.toString()
System.out.println(Arrays.toString(a));
//调用Arrays中方法toString输出[1, 5, 6, 45, 59, 622, 46, 462, 159, 66]
printArray(a);
System.out.println(" ");
//对数组a进行排序 升序
Arrays.sort(a);
System.out.println(Arrays.toString(a));
//fill:对数组值进行填充
Arrays.fill(a,1,5,0);
System.out.println(Arrays.toString(a));
//输出[1, 0, 0, 0, 0, 59, 66, 159, 462, 622],数组中第二个数到第六个数,不包括第六个数,值被覆盖为0
Arrays.fill(a,0);
System.out.println(Arrays.toString(a));
//输出[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],数组的全部值被0覆盖
}
//重复造轮子,自己写一个打印输出数组的方法
public static void printArray(int[] a){
for (int i = 0; i < a.length; i++) {
if(i == 0){
System.out.print("[");
}
if (i == a.length-1){
System.out.print(a[i]+"]");
}else {
System.out.print(a[i]+", ");
}
}
}
}
数组分配机制
数组在默认条件下是引用传递,赋的值是地址
package javaSEStudy.basics.Studay.Array;
//数组分配
public class ArrayAssign {
public static void main(String[] args) {
//基本数据类型赋值
//值拷贝,n1 n2 互不影响
//n2 的值不会影响到n1
int n1 = 10;
int n2 = n1;
// n2 = 80;
// System.out.println(n2);
//数组在默认条件下是引用传递,赋的值是地址,赋值的方式是引用传递,是一个地址
int[] arr1 = {1,2,3,4,5,6};
//把arr1 赋值给 arr2
//但是arr2 的变化会影响到arr1
int[] arr2 = arr1;
arr2[0] = 10;
System.out.println(arr1[0]);
}
}
在这里赋值给了arr2的第一个元素10,但是arr1的第一个元素也变成了10
在内存空间只要分配了一个数据空间,一定会对应一个地址

值传递----->值拷贝
引用传递------>地址拷贝
数组拷贝
package javaSEStudy.basics.Studay.Array;
//数组拷贝
//数据空间独立
public class ArrayCopy {
public static void main(String[] args) {
int[] a = {1,2,3,4,5,6};
//创建一个新的数组b,开辟一个新的数据空间,大小等于a.length
int[] b = new int[a.length];
//遍历a,把每个元素拷贝到b对应位置
for (int i = 0; i < a.length; i++) {
b[i] = a[i];
}
//此时修改b,不会影响到a
b[1]=10;
System.out.println(a[1]);
System.out.println(b[1]);
}
}
通过数组拷贝,自己写程序进行数组添加
char key = scanner.next().charAt(0);
从用户输入中提取首个字符并存入char型变量key,适用于需要单字符控制的场景(如菜单选择/YN确认)
简易版本:
package javaSEStudy.basics.Studay.Array;
import java.util.Scanner;
//实现动态给数组添加元素效果,实现对数组扩容
public class ArrayAdd3 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
//初始化数组
int[] arr = {1,2,3};
do {
int[] arrNew = new int[arr.length + 1];
for (int i = 0; i < arr.length; i++) {
arrNew[i] = arr[i];
}
System.out.println("请输入添加的元素");
int addNum = scanner.nextInt();
arrNew[arrNew.length - 1] = addNum;
arr = arrNew;
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
System.out.println("是否继续添加元素? y/n");
char key = scanner.next().charAt(0);
if (key == 'n') {
break;
}
} while (true);
System.out.println("已退出");
}
}
复杂版本:
package javaSEStudy.basics.Studay.Array;
import java.util.Scanner;
import java.util.Arrays;
public class ArrayAdd2 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 初始空数组
int[] arr = new int[0];
while (true) {
System.out.println(" 当前数组: " + Arrays.toString(arr));
System.out.print(" 请选择操作 [y]添加 / [s]显示 / [n]退出: ");
String choice = scanner.next();
switch (choice) {
case "y":
System.out.print(" 请输入要添加的数字: ");
//注意,数字只能一个一个添加
int num = scanner.nextInt();
// 动态扩容
arr = Arrays.copyOf(arr, arr.length + 1);
arr[arr.length - 1] = num;
System.out.println(" 添加成功!");
break;
case "s":
System.out.println(" 当前数组内容: " + Arrays.toString(arr));
break;
case "n":
System.out.println(" 最终结果: " + Arrays.toString(arr));
scanner.close();
return;
default:
System.out.println(" 输入错误,请重新输入");
}
}
}
}
冒泡排序
冒泡排序是最为出名的排序算法之一,总共有八大排序
理解:两层循环,外层冒泡轮数,里层依次比较
看到嵌套循环应该知道,该算法时间复杂度为O(n2)
让我们举个栗子
public class ArrayDemo07 {
public static void main(String[] args) {
int[] array = {1,6,5,9,33,56,91,51,15};
sort(array);
//调用完方法后,返回一个排序好的数组
System.out.println(Arrays.toString(array));
}
//冒泡排序
//1.比价数组中两个相邻的元素,如果第一个数大于第二个数,就交换他的位置
//2.每一次比较都会产生一个最大的数或一个最小的数
//3.下一轮则可以少一次排序
//依次循环,直到结束
public static int[] sort(int[] a){
//临时变量
//在java中,想把两个数交换位置,得借助一个第三变量,先把第一个数的值给第三个临时变量,再把第二个值赋给第一个
//再把第三个临时变量的值赋给第二个,从而实现两个数交换位置
int temp = 0;
//通过flag标识符减少没有意义的比较
boolean flag = false;
//外层循环,判断程序进行多少次
for (int i = 0; i < a.length-1; i++) {
//内层循环,比较两个数大小,如果第一个数大于第二个数,则交换位置
for (int j = 0; j < a.length-1-i; j++) {
if (a[j+1] < a[j]){ //该写法从小到大排序,如果想从大到小排序则将此处的小于改为大于
temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
flag = true;
}
}
//条件判断,当所有flag都为false,跳出循环
if (flag == false){
break;
}
}
//将值返还到数组中
return a;
}
}
让我们将scanner输入与排序结合一下
package Studay.Array;
import java.util.Arrays;
import java.util.Scanner;
public class ArrayDemo08 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入整数(回车键分行):");
int[] array = new int[10];
int index = 0; //索引变量,记录当前正在填充数组元素的位置
while (scanner.hasNextInt()){ //循环读取整数,直到无整数可读
array[index] = scanner.nextInt(); //读取一个整数并存储在当前索引位置
index++; //移动索引,为存储下一个整数做准备
if (index >= array.length){ //若填满数组则退出循环
break;
}
}
scanner.close();
sort(array);
//调用完方法后,返回一个排序好的数组
System.out.println(Arrays.toString(array));
}
//冒泡排序
//1.比价数组中两个相邻的元素,如果第一个数大于第二个数,就交换他的位置
//2.每一次比较都会产生一个最大的数或一个最小的数
//3.下一轮则可以少一次排序
//依次循环,直到结束
public static int[] sort(int[] a){
//临时变量
//在java中,想把两个数交换位置,得借助一个第三变量,先把第一个数的值给第三个临时变量,再把第二个值赋给第一个
//再把第三个临时变量的值赋给第二个,从而实现两个数交换位置
int temp = 0;
//通过flag标识符减少没有意义的比较
boolean flag = false;
//外层循环,判断程序进行多少次
for (int i = 0; i < a.length-1; i++) {
//内层循环,比较两个数大小,如果第一个数大于第二个数,则交换位置
for (int j = 0; j < a.length-1-i; j++) {
if (a[j+1] < a[j]){ //该写法从小到大排序,如果想从大到小排序则将此处的小于改为大于
temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
flag = true;
}
}
//条件判断,当所有flag都为false,跳出循环
if (flag == false){
break;
}
}
//将值返还到数组中
return a;
}
}
第二个即为对动态数组进行处理,需前后贯通,才能更好理解
稀疏数组
需求:编写五子棋游戏,有存盘退出和续上盘的需求

分析问题:因为该二维数组的很多值为0,因此记录了很多没有意义的数据
稀疏数组介绍
- 当一个数组中大部分元素为0,或者同一值的数组时,可以使用稀疏数组来保存该数组
它的处理方式为: - 记录数组有几行几列,有多少个不同的值
- 把具有不同值的元素和行列及值记录在一个小规模的数组中,从而缩小程序的规模
左边为原始数组,右边为稀疏数组
![]()
该图的6 7 8为六行七列存了8个值,下面则为该数字的坐标
以五子棋游戏为题,将其用代码写出稀疏数组
public class ArrayDemo09 {
public static void main(String[] args) {
//创建一个二维数组11*11 0:没有棋子 1:黑棋 2:白棋
int[][] array1 = new int[11][11];
array1[1][2] = 1;
array1[2][3] = 2;
//输出原始数组
for(int[] ints : array1){ //打出该数组快速打发:array1.for
for (int anInt : ints){
System.out.print(anInt+"\t");
}
System.out.println();
}
System.out.println("=======================");
//转换为稀疏数组保存
//1.获取有效值的个数
int sum = 0;
for (int i = 0; i < 11; i++) {
for (int j = 0; j < 11; j++) {
if (array1[i][j] != 0){
sum++;
}
}
}
System.out.println("有效值的个数为:"+sum);
//2.创建一个稀疏数组的数组
int[][] array2 = new int[sum+1][3];
array2[0][0] = 11;
array2[0][1] = 11;
array2[0][2] = sum;
//3.遍历二维数组,将非零的值存放到稀疏数组中
int count = 0;
for (int i = 0; i < array1.length; i++) {
for (int j = 0; j < array1[i].length; j++) {
if (array1[i][j] != 0){
count++;
array2[count][0] = i;
array2[count][1] = j;
array2[count][2] = array1[i][j];
}
}
}
//输出稀疏数组
System.out.println("稀疏数组");
for (int i = 0; i < array2.length; i++) {
System.out.println(array2[i][0]+"\t"+array2[i][1]+"\t"+array2[i][2]+"\t");
}
System.out.println("===============");
System.out.println("还原数组");
//1.读取稀疏数组
int[][] array3 = new int[array2[0][0]][array2[0][1]];
//2.给其中的元素还原他的值
for (int i = 1; i < array2.length; i++) {
array3[array2[i][0]][array2[i][1]] = array2[i][2];
}
//3.打印还原数组
for(int[] ints : array3){ //打出该数组快速打发:array1.for
for (int anInt : ints){
System.out.print(anInt+"\t");
}
System.out.println();
}
}
}
这样写可以在存储大型数据时节省很多存储空间

浙公网安备 33010602011771号