数组
一、什么是数组
数组是存储同一数据
类型多个元素的集合。
1)既可以存放基本数据类型元素,也可以存放引用数据类型元素(比如String就是一种引用数据类型)
2)数组本身是引用数据类型
3)初始化之后长度不可改变
二、为什么要有数组
假如我们要做一个用来签到的小程序,假设有三个学生,把他们的签到情况设置成boolean类型,如果他们来签到了,那么设为true,否则为false。那我们要怎么做呢,那我们要建立三个boolean类型的变量,然后分别操作。如下:
package com.daw.test1;
import java.util.Scanner;
public class Test2 {
public static void main(String[] args) {
//定义三个学生的签到情况
boolean stu0 = false;
boolean stu1 = false;
boolean stu2 = false;
Scanner scanner = new Scanner(System.in);
int id = -1; //id表示当前签到学生的编号
while(true){
System.out.print("系统闲置,请输入签到编码:");
id = scanner.nextInt();
//判断编码属于哪个学生,并签到
if(id == 0){
stu0 = true;
}else if (id == 1) {
stu1 = true;
}else if (id == 2) {
stu2 = true;
}else {
//注意,一个好的程序应当尽量应对到所有可能
System.out.println("无效的编码,请重新输入!");
continue;
}
System.out.println("签到成功!");
if((stu0==true) && (stu1==true) && (stu2==true)){
//所有学生都签到完成后,跳出程序
System.out.println("全勤!");
break;
}
}
}
}
虽然是个错误示范,但希望没什么基础的同学也敲一敲,也当复习上次内容了。
可见,整个程序很长,而且很多操作都是重复的。况且每个学生的签到情况都有自己的名字,管理起来也及其不方便。这个人就得写这么多,如果一个班五十多人,甚至一百人,公司一千人,以这样的方式,程序员不累死也得烦死。
本着不迫害程序员的态度,数组就诞生了。上面的程序,如果用数组来实现,将会是这样的:
package com.daw.test1;
import java.util.Scanner;
public class Test2 {
public static void main(String[] args) {
//定义10个学生的签到情况
boolean[] stu = new boolean[10]; //此处并没有给它们赋值,但它们却是各个都有值的,详情下面讲
Scanner scanner = new Scanner(System.in);
int id = -1; //id表示当前签到学生的编号
while(true){
System.out.print("系统闲置,请输入签到编码:");
id = scanner.nextInt();
//判断编码属于哪个学生,并签到
if((id>=0) && (id<=10)){
stu[id] = true;
}else {
//注意,一个好的程序应当尽量应对到所有可能
System.out.println("无效的编码,请重新输入!");
continue;
}
System.out.println("签到成功!");
//用于标记是否所有人都已经签到
boolean flag = false;
for(int i = 0; i < 10; i++){
if (stu[i] == false) {
flag = false;
break;
}
//所有人都签到了
flag = true;
}
if (flag == true) {
System.out.println("全勤");
break;
}
}
}
}
看上去好像和上面长度差不多,但实际上这个稍微改改数字,就可以完成一千人,一万人的签到。而上面的则需要不断地重复写相似的内容,人数越多,代码越多。
三、数组的定义
两种方式:
数据类型[] 数组名;
数据类型 数组名[];
例:
int[] nums;
int nums[];
四、数组的初始化
1)静态初始化—初始化时指定每个数组元素的初始值,由系统决定数组长度
int[] arr = new int[]{1, 2, 3};
int[] arr = {1, 2, 3};
2)动态初始化—初始化时只指定数组长度,由系统为数组分配初始值
int[] arr = new int[5];
3)数组的长度—数组名.length
如arr.length
4)访问数组中的元素
数组名[下标]
下标的范围 0~数组的长度-1
我们编码试一试,敲一段,运行看看结果,然后注释掉已经运行完的部分,再往下敲。不要一次性敲完,不然输出一堆,懒得看结果。
package com.daw.test;
public class Test01 {
public static void main(String[] args) {
//静态初始化一个数组
//int[] arr = new int[]{1,2,3};
int[] arr = {188,255,3646};
//打印数组长度
System.out.println(arr.length);
//打印数组中元素
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
System.out.println("----------------");
//动态初始化一个数组 ---- 探究基本数据类型(8)的初始值
//整数
byte[] arrByte = new byte[3];
int[] arrInt = new int[3];
short[] arrShort = new short[3];
long[] arrLong = new long[3];
System.out.println(arrByte[0]);
System.out.println(arrInt[0]);
System.out.println(arrShort[0]);
System.out.println(arrLong[0]);
System.out.println("----------------");
//小数
double[] arrDouble = new double[3];
float[] arrFloat = new float[3];
System.out.println(arrDouble[0]);
System.out.println(arrFloat[0]);
System.out.println("----------------");
//布尔
boolean[] arrBoolean = new boolean[3];
System.out.println(arrBoolean[0]);
System.out.println("----------------");
//字符
char[] arrChar = new char[3];
System.out.println(arrChar[0]);
System.out.println(arrChar[0] + 0);
/*
* 基本数据类型的初始值为 0
* */
//探究引用型数据类型(自定义)的初始值
String[] arrString = new String[10];
System.out.println(arrString[9]);
System.out.println("----------------");
Student[] arrStudent = new Student[3];
System.out.println(arrStudent[0]);
System.out.println("----------------");
/*
* 引用数据类型的初始值为null
* */
}
}
总结:动态初始化时,系统为每个元素指定初始值
基本数据类型
|-byte 0
|-short 0
|-int 0
|-long 0
|-double 0.0
|-float 0.0
|-boolean false
|-char 0
引用数据类型----null
|-String null
五、常见问题
1)ArrayIndexOutOfBoundsException–下标越界异常
出现此错误说明你在访问数组时,超出了数组的范围。
package com.daw.test;
public class Test02 {
public static void main(String[] args) {
int[] arr = new int[10];
//ArrayIndexOutOfBoundsException --- 数组下标越界异常,操作的位置超出数组长度
System.out.println(arr[10]);
}
}
2)NullPointerException–空指针异常
不止在数组中出现,此错误说明,你使用的对象并未初始化 或 指向null。
package com.daw.test;
public class Test02 {
public static void main(String[] args) {
int[] arr = new int[10];
//NullPointerException --- 空指针异常,操作的指针为空
arr = null;
System.out.println(arr.length);
}
}
六、数组常见操作
1)遍历
可有三种方式实现:
package com.daw.test;
public class Test03 {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] arr = {100, 99, 101, 200, 1};
//for循环
for(int index = 0; index < arr.length; index++){
System.out.println(arr[index]);
}
System.out.println("**********");
//while循环
int index = 0;
while(index < arr.length){
System.out.println(arr[index]);
index++;
}
System.out.println("**********");
//增强for循环 --- foreach --- 可以遍历一切多项的变量
for(int item : arr){ //每次遍历都将arr中的一项赋值到item中
System.out.println(item);
}
System.out.println("**********");
}
}
2)求最值
package com.daw.test;
public class Test04 {
public static void main(String[] args) {
int[] arr = {5, 4, 1000, -9999999, 6};
//最大值
int max = arr[0];
for(int i : arr){
if(max < i){
max = i;
}
}
System.out.println(max);
//最小值
int min = arr[0];
for(int i : arr){
if(min > i){
min = i;
}
}
System.out.println(min);
}
}
3)逆序
package com.daw.test;
public class Test05 {
public static void main(String[] args) {
int[] arr = {1, 4, 9, 3, 2};
for(int i = 0, j = arr.length - 1; i < j; i++, j--){ //for循环,初始化语句有多条时,用","分割,而不是";"
//变量交换
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
for(int i = 0; i < arr.length; i++){
System.out.println(arr[i]);
}
}
}
4)选择排序
|---选定一个元素和其后的元素进行比较
|---每一轮比较完成都会选出最大或最小的元素
5)冒泡排序
|---相邻元素两两进行比较
|---如果数组已经排好序,如何减少比较次数
数据结构中较为简单的算法,根据要求自己尝试编写。
七、数组一旦初始化,长度不可改变
涉及计算机基础知识,初学者知道这一性质即可。
在计算机内存中,有系统栈和系统堆两块儿区域,我们所声明的变量的名和值
会放置在系统栈的一块区域中,但栈中每块区域的大小是特定的。而数组等引用类型
的数据大小可能会特别的大,而且长度也并不像基本数据类型那样确定。但为了在进行调用的时候所有的对象的操作保持一致,因此在系统栈中,引用数据类型的“值”实际上是一块地址,其真正的值存贮在对应的地址上的堆中。当一个数组初始化时,就会在堆内开辟确定长度的一块儿区域。
比如我们打印一下我们初识化过得数组:
System.out.println(arr);
会发现打印出来一堆乱七八糟的有数字有符号,有字母。其实那表示一个地址。
而试图改变数组长度,就像盖房子一样,去申报得到了一块儿地,但是你盖着盖着发现你想要把房子盖的更大,但是你周围的地政府可能已经给别人了,或者有其他的目的和打算,你多盖一些,他多盖一些,就把整个政府的计划给搞乱了。为了避免这种情况,如果你盖着盖着,发现地不够了,那你可以去重新申请一片更大的地,这片地就归还国家了。
图示,假设我定义如下;
int i = 5;
float f =1.63;
int[] arr = new int[4];
那他们在系统栈和堆上是这样的。
假如,我们觉得arr不够用,于是我们又申请了另一个长度为5的数组
arr = new int[7];
可以看出,当你更换了区域以后,原来的数据并不会改变,只不过它不再属于你了而已。这就像前女…
总结:
栈:
基本数据类型变量存值
引用数据类型变量存址
堆:
对象,数组---通过new生成的区域
八、二维数组&多维数组
在实际开发中使用很少。二维数组,本质就是以一维数组为元素的一维数组。三维数组也一样,就是以二维数组为元素的一维数组,多维数组同理。
所以啊,它就是个一维数组,你知道了它的定义方式,拿着以为数组的初始化套着玩玩就行了。
定义也很简单:
//二维数组
int[][] arr = new int[5][];
//三维数组
int[][][] arr1 = new int[5][][];
想要几维数组,就几个括号,至于括号在变量名前后,都可,甚至可以几个在前几个在后。这也表明了,多维数组的实质就是一维数组,只不过我们之前学的一维数组的元素都不是数组而已。所以多维数组也可以使用所有一维数组的操作。
需要注意的是,定义多维数组时,只必需给出最前面的长度。为啥?因为它就是个一维数组啊,它里面的元素是数组,就是一种引用数据类型,默认值就是null呗,所以里面的元素你想在一开始赋值就赋值,不想赋值就算了。
它就是一个一维数组。
至于二维数组和多维数组的遍历,相信学过嵌套循环的你已经不会被这种垃圾问题难住了,不多讨论,告辞。