数组的基本定义
数组指的是一组相关变量的集合。如果要定义100个整型变量,按照最原始的方法:
int i2;
...
int i100.
这种操作可以实现要求,但是这些变量的管理太麻烦了。为此,开发中,可以利用数组来解决这一问题。
在Java中,数组是引用数据类型,既然是引用数据类型就涉及内存。对于数组的定义语法有以下两种:
- 声明并开辟数组:数据类型 数组名称[] = new 数据类型 [长度];(数据类型 [] 数组名称= new 数据类型 [长度];)
- 分步完成:
- 声明数组:数据类型 数组名称 [] = null;(数据类型 [] 数组名称 = null)
- 开辟数组:数组名称 = new 数据类型 [长度];
当数组开辟空间后就可以采用“数组[索引]”的形式进行数组访问,但是需要注意的是,如果现在数组的长度为3,索引的范围是0~2(一共三个元素)。操作中如果超过了数组定义的索引范围,则程序在运行过程中会出现数组越界报错(ArrayIndexOutOfBoundsException)。
以上的操作属于数组的动态初始化,动态初始化的特点是数组开辟空间后,数组中每个元素的内容都是其对应数据类型的默认值。
范例:定义数组
public class ArrayDemo {//程序必须有类
public static void main(String args[]){
int data []=new int [3];//开辟一个data数组,长度为3
System.out.println(data[0]);//0
System.out.println(data[1]);//0
System.out.println(data[2]);//0
}
}
范例:数组的赋值操作
public class ArrayDemo {//程序必须有类
public static void main(String args[]){
int data []=new int [3];//开辟一个data数组,长度为3
data[0]=10;//为数组赋值
data[1]=20;
data[2]=30;
System.out.println(data[0]);//10
System.out.println(data[1]);//20
System.out.println(data[2]);//30
}
}
添加一条语句
System.out.println(data[3]);
但是数组本身是一种顺序式的结构,所以在进行数组内容输出的时候,往往可以采用循环的方式。由于数组长度是固定的,所以可以考虑for循环对数组进行输出。而数组长度在Java中可以使用“数组对象.length”属性取得。
范例:数组输出
public class ArrayDemo {//程序必须有类
public static void main(String args[]){
int data []=new int [3];//开辟一个data数组,长度为3
System.out.println(data.length);
data[0]=10;
data[1]=20;
data[2]=30;
for(int i=0;i<data.length;i++){
System.out.print(data[i]+" ");
}
}
}
数组的引用分析
数组的引用分析与对象类似,以如下代码为例:
public class ArrayDemo {//程序必须有类
public static void main(String args[]){
int data []=new int [3];//开辟一个data数组,长度为3
System.out.println(data.length);
data[0]=10;
data[1]=20;
data[2]=30;
for(int i=0;i<data.length;i++){
System.out.print(data[i]+" ");
}
}
}
以上操作与普通对象操作本质上的唯一区别在于普通的类对象保存属性,利用属性名称来操作,而数组保存的是一组内容,利用索引来操作。分步实例化数组对象同理。
范例:分步实例化数组对象
了解了上述内容,就可以进行数组的引用操作了,引用的本质就是同一块堆内存空间被不同的栈内存空间所指向。
例:数组的引用操作
public class ArrayDemo {//程序必须有类
public static void main(String args[]){
int data [] = new int [3];//开辟一个data数组,长度为3
data[0]=10;
data[1]=20;
data[2]=30;
int temp[]=data;//发生了引用操作
temp[0]=100;
for(int i=0;i<data.length;i++){
System.out.print(data[i]+" ");//100 20 30
}
}
}
通过内存关系图来进行操作描述。
数组的静态初始化
以上是针对数组动态初始化,动态初始化操作的特点在于其只能够在开辟数组空间之后进行数组内容的赋值,如果现在希望数组开辟之后就可以存在明确的内容,那么则使用数组的静态初始化,而语法有如下两种:
第一种:简化型
数据类型 数组名称[]={值,值,…,值};
数据类型 [] 数组名称={值,值,…,值};
第二种:完全型(推荐使用)
数据类型 数组名称[]=new 数据类型[]{值,值,…,值};
数据类型 [] 数组名称=new 数据类型[]{值,值,…,值};
范例:使用数组的静态初始化
public class ArrayDemo {//程序必须有类
public static void main(String args[]){
int data [] = new int []{10,20,30};//开辟一个data数组,长度为3
for(int i=0;i<data.length;i++){
System.out.print(data[i]+" ");//10 20 30
}
}
}
范例:判断某个数字是否在数组中存在
最简单的做法是进行for循环操作,依次与每一个数组中的内容进行比较。
public class ArrayDemo {//程序必须有类
public static void main(String args[]){
int sdata = 11;//要查找的数据
boolean flag = false;//保存查找结果,如果找到修改为true
int data [] = new int []{10,20,30,11,22,33};//开辟一个data数组,长度为3
for(int i=0;i<data.length;i++){
if(data[i]==sdata){//现在已经查找到了内容
flag=true;
break;//退出循环
}
}
if (flag){
System.out.println("该数据已经查找到!");
}else{
System.out.println("查无此数据!");
}
}
}
但是严格来讲,此代码性能并不好,因为它需要将数组中的每一个元素都进行判断。
面试题:请解释什么叫二分查找法(折半查找法)
二分查找法的重要支持:数组中的数据必须是有序的。
二维数组(理解)
在开发之中,之前使用的数组只有一个索引下标,所以可以简单的将其理解为是一个一维数组。一位数组的特点是可以根据索引号确定内容。
索引号 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
数据 | 100 | 15 | 42 | 63 |
二维数组实际上与数据表的形式完全相同,由行和列组成,那么要想确定一个数据,必须有行和列的编号,所以二维数组的组成如下:
行索引、列索引 | 列索引0 | 列索引1 | 列索引2 | 列索引3 | 列索引4 |
---|---|---|---|---|---|
行索引0 | 4 | 33 | 65 | 543 | 9 |
行索引1 | 66 | 47 | 39 | 55 | 21 |
行索引2 | 9 | 57 | 8 | 34 | 2 |
现在想确定55这个数据,那么就需要行索引为1,列索引也为3才能定位到。
对于二维数组的定义有两种语法结构:
- 动态初始化
数据类型 数组名称[][]=new 数据类型[行个数][列个数] - 静态初始化
数据类型 数组名称[][]=new 数据类型[][]{
{数据,数据,…}
{数据,数据,…}
…}
实际上二维数组的本质就是在一维数组的基础上再嵌套了一层数组。
范例:观察二维数组使用
public class ArrayDemo {//程序必须有类
public static void main(String args[]){
int data[][]=new int [][]{
{1,2},{4,5,6},{7,8,9,10}
};
for(int x=0;x<data.length;x++){//控制数组行
for(int y=0;y<data[x].length;y++){
System.out.print(data[x][y]+" ");
}
System.out.println();
}
}
}
面试题:实现二维数组转置
public class ArrayDemo {//程序必须有类
public static void main(String args[]){
int data[][]=new int [][]{
{1,2,3},{4,5,6},{7,8,9}
};
for(int x=0;x<data.length;x++){//控制数组行
for(int y=0;y<=x;y++){
if(x!=y){
int temp=data[x][y];
data[x][y]=data[y][x];
data[y][x]=temp;
}
}
}
for(int x=0;x<data.length;x++){//控制数组行
for(int y=0;y<data[x].length;y++){
System.out.print(data[x][y]+" ");
}
System.out.println();
}
}
}
这些都属于对于数组的逻辑控制,本身只能够作为扩展大脑思维使用的东西,对实际开发帮助不大。
数组与方法
在数组与方法进行传递的过程中,实际上就属于引用传递。在数组与方法间的操作中,主要考虑两种形式:方法接收数组,方法返回数组。但无论是接收还是返回,最终操作一定是一个堆内存的使用。
范例:利用方法接收数组
public class ArrayDemo {//程序必须有类
public static void main(String args[]){
int [] data = new int []{1,3,5,7,9};//是一个数组
printArray(data);
}
public static void printArray(int temp[]){//接收一个数组
for (int x=0;x<temp.length;x++){
System.out.print(temp[x]+" ");//1 3 5 7 9
}
System.out.println();//换行
}
}
整个的操作过程与数组的引用接手时完全一样的,但是这个时候的代码只是针对当前的数组内容进行简单的输出,于是下面可以进一步加强,例如:设置一个方法,在此方法中实现数组的乘2操作。
范例:方法里面修改数组内容
public class ArrayDemo {//程序必须有类
public static void main(String args[]){
int [] data = new int []{1,3,5,7,9};//是一个数组
inc(data);//int arr[]=data;
printArray(data);
}
public static void inc(int arr[]){
for(int x=0;x<arr.length;x++){
arr[x]*=2;//数组中的每个元素乘2保存
}
}
public static void printArray(int temp[]){//接收一个数组
for (int x=0;x<temp.length;x++){
System.out.print(temp[x]+" ");
}
System.out.println();//换行
}
}
在大部分情况下,如果某一个方法可以接收的数据是引用数据类型,往往都不需要设置返回值。
范例:数据排序操作(理解)
数组中可以保存多个数据,但是在很多情况下(例如:二分查找法)必须针对数组中的数据进行排序,这个时候就需要进行一些复杂的处理。
通过分析可以发现,排序并不是一次完成的,而是需要多次,也就是说至少需要“数组长度”减一次
public class ArrayDemo {//程序必须有类
public static void main(String args[]){
int [] data = new int []{44,23,6,88,32,54,9,14,39};//是一个数组
for(int x=0;x<data.length-1;x++){
for(int y=0;y<data.length-1;y++){
if(data[y]>data[y+1]){
int temp = data[y];
data[y]=data[y+1];
data[y+1]=temp;
}
}
System.out.print("第"+(x+1)+"次排序:");
printArray(data);
}
}
public static void printArray(int temp[]){//接收一个数组
for (int x=0;x<temp.length;x++){
System.out.print(temp[x]+" ");
}
System.out.println();//换行
}
}
此时的确实现了排序操作,但主方法里的代码过多。主方法是日后程序调用的客户端,在客户端操作的代码中不应该编写过多的复杂操作,而应该进行简化处理
面试题:请编写一个数组排序操作
public class ArrayDemo {//程序必须有类
public static void main(String args[]){
int [] data = new int []{44,23,6,88,32,54,9,14,39};//是一个数组
sort(data);//客户端调用越简单越好
printArray(data);
}
public static void sort(int arr[]){
for(int x=0;x<arr.length-1;x++){
for(int y=0;y<arr.length-1;y++){
if(arr[y]>arr[y+1]){
int temp = arr[y];
arr[y]=arr[y+1];
arr[y+1]=temp;
}
}
}
}
public static void printArray(int temp[]){//接收一个数组
for (int x=0;x<temp.length;x++){
System.out.print(temp[x]+" ");
}
System.out.println();//换行
}
}
范例:数组的反转操作
就是将一个数组进行首位交换,但是交换的实现方式有两种:
- 第一种思路:
- 定义一个新的数组,而后将原始数组的数据逆序输出,随后改变原始数组的引用。
public class ArrayDemo {//程序必须有类
public static void main(String args[]){
int data[] = new int []{44,23,6,88,32,54,9,14,39};//是一个数组
int newArr[]=new int [data.length];//声明一个与原始数组大小相同的数组
int foot=data.length-1;//定义原始数组的操作脚标
for(int x=0;x<newArr.length;x++){
newArr[x]=data[foot--];//逆序保存
}
data=newArr;//改变原始数字引用
printArray(data);
}
public static void printArray(int temp[]){//接收一个数组
for (int x=0;x<temp.length;x++){
System.out.print(temp[x]+" ");
}
System.out.println();//换行
}
}
通过内存关系图可以发现,以上代码虽然可以实现,但是会产生垃圾,不建议使用。
第二种方法:在一个数组上实现转置
长度为基偶数的情况与之类似。
public class ArrayDemo {//程序必须有类
public static void main(String args[]){
int data[] = new int []{44,23,6,88,32,54,9,14,39};//是一个数组
reverse(data);
printArray(data);
}
public static void reverse(int arr[]){
int center=arr.length/2;
int head=0;
int tail=arr.length-1;
for(int x=0;x<center;x++){
int temp=arr[head];
arr[head]=arr[tail];
arr[tail]=temp;
head++;
tail--;
}
}
public static void printArray(int temp[]){//接收一个数组
for (int x=0;x<temp.length;x++){
System.out.print(temp[x]+" ");
}
System.out.println();//换行
}
}
以上都是方法接收数组的形式,下面看下方法返回数组的操作。如果某一个方法要返回数组,只需要将方法的返回值类型声明为数组。
范例:方法返回数组
public class ArrayDemo {//程序必须有类
public static void main(String args[]){
int data[] = init();
printArray(data);
}
public static int[] init(){//返回一个数组
return new int []{44,23,6,88,32,54,9,14,39};//是一个数组
}
public static void printArray(int temp[]){//接收一个数组
for (int x=0;x<temp.length;x++){
System.out.print(temp[x]+" ");
}
System.out.println();//换行
}
}
如果返回的是引用数据类型,那么返回null语法上是没有任何问题的。
与数组有关的类库
Java本身提供一系列的开发类库帮助用户进行代码编写,下面首先给出两个基本的数组操作方法。
1 数组拷贝:以下定义与原始定义有出入,以后再进行细化。
- 拷贝:System.arraycopy(原始数组,原始数组开始点,目标数组,目标数组的开始点,拷贝长度)。
范例:数组拷贝- 原始数组1:1,2,3,4,5,6,7,8,9;
- 原始数组2:10,20,30,40,50,60,70,80,90;
- 希望实现数组2的部分内容替换掉数组1的部分内容:1,60,70,80,5,6,7,8,9.
public class ArrayDemo {//程序必须有类
public static void main(String args[]){
int data1[] = new int[]{1,2,3,4,5,6,7,8,9};
int data2[] = new int[]{10,20,30,40,50,60,70,80,90};
System.arraycopy(data2,5,data1,1,3);
printArray(data1);
}
public static void printArray(int temp[]){//接收一个数组
for (int x=0;x<temp.length;x++){
System.out.print(temp[x]+" ");
}
System.out.println();//换行
}
}
2 数组排序
- 语法:java.util.Arrays.sort(数组名称).
范例:数组排序
public class ArrayDemo {//程序必须有类
public static void main(String args[]){
int data[] = new int[]{44,23,6,88,32,54,9,14,39};
java.util.Arrays.sort(data);
printArray(data);
}
public static void printArray(int temp[]){//接收一个数组
for (int x=0;x<temp.length;x++){
System.out.print(temp[x]+" ");
}
System.out.println();//换行
}
}
这种方式是在代码开发中使用的,面试的时候 可不推荐用,要写算法细节。
总结
- 数组属于引用数据类型;
- 数组属于线性存储结构,里面的内容可以根据索引线性操作;
- 数组里面容易出现面试题。
对象数组(重点)
对象数组的使用及内存的分配操作。
对象数组的核心概念就是:某一个数组之中,保存的都是对象。所以对于对象数组的定义格式有以下两种:
- 动态初始化:类名称 对象数组名称[] = new 类名称[长度];
- 静态初始化:类名称 对象数组名称[] = new 类名称{实例化对象,实例化对象,…};
范例:使用动态初始化定义对象数组
class Person{
private String name;
private int age;
public Person(String n,int a){
name = n;
age = a;
}
public String getInfo(){
return "姓名:"+name+","+"年龄:"+age;
}
//无参构造、setter、getter略
}
public class ArrayDemo {//程序必须有类
public static void main(String args[]){
Person per[] = new Person[3];//开辟了有三个元素的对象数组
per[0]=new Person("张三",19);
per[1]=new Person("李四",20);
per[2]=new Person("王五",21);
for(int x=0;x<per.length;x++){
System.out.println(per[x].getInfo());
}
}
}
使用动态初始化操作形式产生的对象数组,里面的每个元素都是null()。
类本身属于引用数据类型,那么所有的对象都一定会保存在堆内存空间,而对象数组包含的是一组对象,所以这个时候如果非要进行内存关系图的描述,则会出现如下结果:
除了动态初始化之外,也可以进行静态初始化的操作。
范例:静态初始化对象数组
class Person{
private String name;
private int age;
public Person(String n,int a){
name = n;
age = a;
}
public String getInfo(){
return "姓名:"+name+","+"年龄:"+age;
}
//无参构造、setter、getter略
}
public class ArrayDemo {//程序必须有类
public static void main(String args[]){
Person perA=new Person("张三",19);
Person perB=new Person("李四",20);
Person perC=new Person("王五",21);
Person per[] = new Person[]{perA,perB,perC};
for(int x=0;x<per.length;x++){
System.out.println(per[x].getInfo());
}
}
}
范例:静态初始化对象数组
class Person{
private String name;
private int age;
public Person(String n,int a){
name = n;
age = a;
}
public String getInfo(){
return "姓名:"+name+","+"年龄:"+age;
}
//无参构造、setter、getter略
}
public class ArrayDemo {//程序必须有类
public static void main(String args[]){
Person per[] = new Person[]{
new Person("张三",19),
new Person("李四",20),
new Person("王五",21)};
for(int x=0;x<per.length;x++){
System.out.println(per[x].getInfo());
}
}
}
用哪种形式取决于自己当前的环境。
总结
有了对象数组的概念之后实际上就相当于简单Java类的概念又进一步进行提升。如果是基本数据类型,则数组里面只保存数据,而如果保存了对象,那么每个元素可以保存更多的内容。
数组永远都有自己的缺陷——长度是固定的。